迁移到 uv
我们为什么迁移到 uv¶
我们最近从 poetry 迁移到 uv,因为我们想从它的许多特性中受益,例如:
- 更轻松的依赖管理,内置自动缓存
- 与 poetry 相比,CI/CD 显著更快,尤其是在使用 Astral 团队提供的
caching
功能时 - Cargo 风格的 lockfile,使得更容易采用新的 PEP 功能
我们花了大约 1-2 天时间处理迁移,对结果很满意。平均而言,对于 CI/CD,我们看到我们的任务速度大幅提升。
以下是一些从我们的 CI/CD 运行中提取的任务耗时。
总的来说,我会说一旦我们为单独的 uv
github actions 实现了缓存,我们的任务速度提升了约 3 倍,所需时间减少了约 67%。
任务 | 耗时 (Poetry) | 耗时 (UV) |
---|---|---|
Ruff 格式化 | 1分16秒 | 28秒 (-63%) |
Pyright | 3分3秒 | 39秒 (-79%) |
测试 Python 3.9 | 1分21秒 | 32秒 (-61%) |
测试 Python 3.10 | 1分32秒 | 33秒 (-64%) |
测试 Python 3.11 | 3分19秒 | 2分48秒 (-16%) |
- 请注意,对于 3.11,我从时间中减去了 1分12秒,因为我们为 gemini 增加了大约 60 个测试,为了进行公平比较,我减去了运行 gemini 测试所需的时间。
我们的大部分较重的任务,如 Test Python
任务,并行运行多个 LLM 调用,因此 UV 的缓存加速在那里的效益有所降低。
我们如何迁移¶
我们做的第一件事是使用自动化工具将我们的 poetry lockfile 转换为 uv 兼容的 lockfile。为此,我参考了 Sebastian Ramirez 关于如何进行转换的这个帖子。
步骤 1:使用 uv
运行 pdm
,这将迁移您的 pyproject.toml,并确保删除所有 tool.poetry
部分。您可以在此处查看最初的 pyproject.toml
。
请注意,由于您正在使用 uv
,请确保也删除 pdm
部分以及您的可选组。
# dependency versions for extras
fastapi = { version = ">=0.109.2,<0.116.0", optional = true }
redis = { version = "^5.0.1", optional = true }
diskcache = { version = "^5.6.3", optional = true }
...
[tool.poetry.extras]
anthropic = ["anthropic", "xmltodict"]
groq = ["groq"]
cohere = ["cohere"]
...
[tool.pdm.build]
includes = ["instructor"]
[build-system]
requires = ["pdm-backend"]
build-backend = "pdm.backend"
步骤 2:完成上述操作后,由于您不再使用 poetry
,您需要更新构建系统。如果您只是删除它,默认情况下会使用 setuptools
,如果您使用 license = {text = "MIT"}
声明了您的许可证,这将引发错误。因此,您需要在 pyproject.toml
中添加以下内容。
这在此 UV issue 此处有文档说明,该 issue 记录了 setuptools 无法处理 Metadata 2.4 键的 bug,因此您需要使用 hatchling
作为您的构建后端。
步骤 3:完成上述操作后,运行 uv sync 以生成您的 uv.lock
文件,确保没有依赖问题。
需要了解的新命令¶
现在我们已经从 poetry
迁移到了 uv
,您需要使用一些新命令。
-
uv sync --all-extras --group <您想安装的依赖组>
:这将使用uv
安装项目的所有依赖项,请确保安装您想安装的特定依赖项。例如,如果您正在编写文档,您可以运行uv sync --all-extras --group docs
-
uv run <命令>
:这将在您创建的虚拟环境中运行特定命令。在运行我们的 CI 管道时,我们使用此命令来确保我们的命令使用了正确的环境。
迁移您的工作流¶
我们有一些使用 poetry
的工作流,因此我们需要更新它们以使用 uv
。如下所示,您需要对相关工作流进行一些主要更改:
name: Test
on:
pull_request:
push:
branches:
- main
jobs:
release:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }} # (1)!
- name: Cache Poetry virtualenv
uses: actions/cache@v2
with:
path: ~/.cache/pypoetry/virtualenvs
key: ${{ runner.os }}-poetry-${{ hashFiles('**/poetry.lock') }}
restore-keys: |
${{ runner.os }}-poetry-
- name: Install Poetry
uses: snok/install-poetry@v1.3.1 # (2)!
- name: Install dependencies
run: poetry install --with dev,anthropic # (3)!
- name: Run tests
if: matrix.python-version != '3.11'
run: poetry run pytest tests/ -k 'not llm and not openai and not gemini and not anthropic and not cohere and not vertexai' && poetry run pytest tests/llm/test_cohere
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
- name: Run Gemini Tests
run: poetry run pytest tests/llm/test_gemini # (4)!
env:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
- name: Generate coverage report
if: matrix.python-version == '3.11'
run: |
poetry run coverage run -m pytest tests/ -k "not docs and not anthropic and not gemini and not cohere and not vertexai and not fireworks"
poetry run coverage report
poetry run coverage html
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
-
我们改用
uv
来安装 python -
我们改用 astral 的
astral-sh/setup-uv@v4
action 来安装uv
-
使用
uv sync
明显比 poetry install 快得多,并且有缓存我想它会更快。 -
我们不再使用
poetry run
,而是使用uv run
,它会启动带有依赖项的 python 虚拟环境,然后运行您传入的命令。
然后我们将工作流修改为以下 yml 配置:
name: Test
on:
pull_request:
push:
branches:
- main
jobs:
release:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v2
- name: Install uv
uses: astral-sh/setup-uv@v4
with:
enable-cache: true # (1)!
- name: Set up Python
run: uv python install ${{ matrix.python-version }}
- name: Install the project
run: uv sync --all-extras
- name: Run tests
if: matrix.python-version != '3.11'
run: uv run pytest tests/ -k 'not llm and not openai and not gemini and not anthropic and not cohere and not vertexai' # (2)!
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
- name: Run Gemini Tests
if: matrix.python-version == '3.11'
run: uv run pytest tests/llm/test_gemini
env:
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
- name: Generate coverage report
if: matrix.python-version == '3.11'
run: |
uv run coverage run -m pytest tests/ -k "not docs and not anthropic and not gemini and not cohere and not vertexai and not fireworks"
uv run coverage report
uv run coverage html
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
-
不要忘记启用缓存,以便您的任务更快
-
在这里使用
uv run
很重要,因为如果您只运行pytest
,它不会在您的虚拟环境中运行测试,导致测试失败。
基本就是这样!大部分迁移工作实际上是试图找出导致测试失败的原因,然后慢慢修复它们。我们能够轻松升级许多现有依赖项,并确保一切都按预期工作。
我们也刚刚用 uv 进行了第一次发布,非常成功!
结论¶
我们对结果很满意,并且很高兴迁移到 uv。这是一个平稳的过渡,我们已经在 CI/CD 任务中看到了显著的加速。我们期待未来继续使用 uv。