3 minute read

I have recently been working on a template for new Python projects and of the first decisions I had to make was which tool to use for packaging and dependency management. I initially chose Poetry since I am already familiar with it: Poetry is a mature tool that is widely used and works well. But several people have recently recommended uv as an alternative so I decided to try it. This post documents the steps I had to take to switch from Poetry to uv.

To summarise the outcome of my testing: uv is extremely fast (it is written in rust), manages python versions in addition to dependencies and is likely to become the new standard so I am adopting it for my new projects.

From a google search, I saw that there are tools to automate this migration (e.g. migrate-to-uv) but for a simple Python repository there are not that many steps to do it manually so that is what I did.

I also had to make additional changes to use uv in my CI/CD (GitHub workflow) pipelines, readthedocs configuration, dependabot configuration, pre-commit hooks and just recipes and to get release-please (which I am using to automate releases) to bump the version number in the uv lock file.

You can see the actual changes I made to some of the files in this pull request.

How to switch from Poetry to uv

I was using Poetry version 2.2.0 and switched to uv version 0.9.4.

Pre-requisites

Basic steps

  1. Open a terminal and navigate to the root of your repository.
  2. Create and switch to a new branch.
  3. Delete the poetry.lock file:
    rm poetry.lock
    
  4. Install uv:
    curl -LsSf https://astral.sh/uv/install.sh | sh
    
  5. Check installation (should show usage details):
    uv
    
  6. Create a uv.lock file:
    uv lock
    
  7. Test uv run with a development tool in your repository (e.g. ruff, black, pytest):
    uv run ruff check .
    
  8. Change the build backend in pyproject.toml to uv_build (or use another build system to suit your needs):
    [build-system]
    requires = ["uv-build>=0"]    # or pin a version
    build-backend = "uv_build"
    
  9. Add a module name for the build (replace package_name with the name of your package):
    [tool.uv.build-backend]
    module-name = "package_name"
    module-root = ""
    
  10. Commit your changes.

Additional steps

These will be different for different repositories. Test that everything works as expected after each change.

Change poetry run to uv run

Change all poetry run commands to uv run in files that use them, e.g.:

  • justfile (see my template’s justfile for examples)
  • makefile
  • devcontainer.json
  • GitHub workflows
  • Pre-commit configuration file

Add a pre-commit hook for uv sync

You can add a pre-commit hook from uv to check that the uv.lock is up-to-date:

repos:
  - repo: https://github.com/astral-sh/uv-pre-commit
    # uv version.
    rev: 0.9.4
    hooks:
      - id: uv-lock

Use uv in your Dockerfile

If you use a Dockerfile, you can install uv by adding the following:

COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv

Then install your dependencies using uv instead of poetry:

uv sync --all-groups

Use uv in GitHub workflows

To use uv in GitHub workflows, replace your poetry setup with the following:

- name: Install uv
  uses: astral-sh/setup-uv@v7
  with:
    version: "0.9.4"
    enable-cache: true

- name: Install dependencies
  run: uv sync --all-groups

Update dependabot config file

If you use dependabot, update the config file:

- package-ecosystem: "uv"

Edit release-please configuration

If you use release-please, add uv.lock as an extra file in the release-please-config.json so that the version number of your package listed there will be bumped:

"extra-files": [
    {
      "jsonpath": "$.package[?(@.name.value=='package-name')].version",
      "path": "uv.lock",
      "type": "toml"
    }
  ]

Edit Read the Docs configuration

If you are using Read the Docs to host your documentation, edit your readthedocs.yaml to use uv to install docs dependencies:

jobs:
  pre_create_environment:
      - asdf plugin add uv
      - asdf install uv latest
      - asdf global uv latest
  create_environment:
      - uv venv "${READTHEDOCS_VIRTUALENV_PATH}"
  install:
      - UV_PROJECT_ENVIRONMENT="${READTHEDOCS_VIRTUALENV_PATH}" uv sync --frozen --group docs

Update your README

Update your project README with instructions for how to use uv and optionally add the uv badge (uv ):

[![uv](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json)](https://github.com/astral-sh/uv)

Comments