Migrate from the Charm plugin to the uv plugin

For charms that use uv, Charmcraft has a uv plugin. This guide shows how to migrate from the default Charm plugin to the uv plugin.

Migrating from the Charm plugin provides some benefits, notably not having to maintain a separate requirements.txt file. For package management, uv is much faster than pip.

If the charm to be migrated does not currently use uv, refer to the uv documentation for instructions on how to use uv for a Python project.

Update the project file

The first step is to update the project file to include the correct parts definition. Depending on the history of a specific charm, it may not have an explicitly-included parts section determining how to build the charm. In this case, a parts section can be created as follows:

charmcraft.yaml
parts:
  my-charm:  # This can be named anything you want
    plugin: uv
    source: .
    build-snaps:
      - astral-uv

List the charm’s dependencies

If the project directory doesn’t already contain a pyproject.toml file, create one. Next, list the charm’s dependencies in the file’s dependencies key.

pyproject.toml
[project]
name = "my-charm"
version = "0.0.1"
requires-python = ">=3.10"

# Dependencies of the charm code.
dependencies = [
    "ops>=3,<4",
]

You can also add dependencies to your project’s pyproject.toml file by running:

uv add <dependency>

List charm library dependencies

Charm libraries are distributed either as regular Python packages under the charmlibs namespace, or hosted on Charmhub. Python packages should be listed in the charm’s dependencies.

Unlike the Charm plugin, the uv plugin does not install transitive dependencies for Charmhub-hosted libraries. If any of these charm libraries have PYDEPS, these need to be added to the charm’s dependencies.

To find library dependencies, check each loaded library file for its PYDEPS by running the following command at the root of the charm project:

find lib -name "*.py" -exec awk '/PYDEPS = \[/,/\]/' {} +

Next, in pyproject.toml, list them in the dependencies key.

pyproject.toml
# Dependencies of the charm code and PYDEPS from libraries.
dependencies = [
    "ops>=3,<4",
    "cosl",
    "pydantic",
    "cryptography",
]

Alternatively, you could list the library dependencies in a dependency group called charmlibs-pydeps.

pyproject.toml
[dependency-groups]
# PYDEPS from libraries that the charm uses.
charmlibs-pydeps = [
    "cosl",
    "pydantic",
    "cryptography",
]

To add a dependency to the charmlibs-pydeps dependency group using uv add, run:

uv add --group charmlibs-pydeps <dependency>

Library dependencies are runtime dependencies, and dependency groups are generally intended for development dependencies. However, if the charm uses a lot of library files, you might find a dependency group helpful for distinguishing the dependencies.

This advice doesn’t apply to libraries that are distributed as Python packages. You should list Python packages in dependencies. You don’t need to do anything further for their transitive dependencies to be properly installed.

Lock the dependencies

After defining the project’s dependencies, make sure that the project directory has a uv.lock file by running:

uv lock

Make sure you add this file to version control, so that your charm can be built after a checkout by running charmcraft pack.

Add dependency groups

If the charm has dependency groups that should be included when creating the virtual environment, such as one for charm libraries, the uv plugin’s uv-groups key can be set to include them:

charmcraft.yaml
parts:
  my-charm:
    plugin: uv
    source: .
    build-snaps:
      - astral-uv
    uv-groups:
      - charmlibs-pydeps

Likewise, optional dependencies under the pyproject.toml key project.optional-dependencies can be added with the uv-extras key.

Include extra files

The uv plugin only includes the contents of the src and lib directories as well as the generated virtual environment. If other files were previously included from the main directory, they can be included again using the Dump plugin:

charmcraft.yaml
parts:
  my-charm:
    plugin: uv
    source: .
    build-snaps:
      - astral-uv
    uv-groups:
      - charmlibs-pydeps
  version-file:
    plugin: dump
    source: .
    stage:
      - charm_version