uv - Modern Python Package Manager

Authors:

Cao Tri DO <cao-tri.do@keyrus.com>

Version:

2025-07

Objectives

This article is intended to know all the basics command for uv, the modern Python package manager. It is not a complete documentation, but it will help you to get started with uv. You can find the complete documentation on the official website: https://uv.readthedocs

Basics Commands for uv for your daily work on Python projects

uv is a modern Python package manager that simplifies the management of Python environments and dependencies. It is designed to be user-friendly and efficient, making it easier for developers to work with Python projects.

  1. Installation of uv (if not already installed)

curl -LsSf https://astral.sh/uv/install.sh | sh
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
  1. Start a new project with uv

uv init --package keyrus_tuto_uv

A note that the uv init command creates a new directory named keyrus_tuto_uv and initializes a new uv project in it. You can replace keyrus_tuto_uv with any name you prefer for your project.

Adding the –package option will allow you to directly create a Python package

  1. Navigate to the project directory

cd keyrus_tuto_uv

We can check the content of the project directory with the command: tree. You will see the following structure:

.
├── README.md
├── pyproject.toml
└── src
    └── keyrus_tuto_uv
        └── __init__.py

3 directories, 3 files

and if you look at the pyproject.toml, you already have a very good starting point:

cat pyproject.toml

You should see:

[project]
 name = "keyrus-tuto-uv"
 version = "0.1.0"
 description = "Add your description here"
 readme = "README.md"
 authors = [
     { name = "Cao Tri DO", email = "cao-tri.do@keyrus.com" }
 ]
 requires-python = ">=3.12"

 [project.scripts]
 keyrus-tuto-uv = "keyrus_tuto_uv:main"

 [build-system]
 requires = ["uv_build>=0.8.17,<0.9.0"]
 build-backend = "uv_build"
  1. To create a virtual environment, you can use the following command:

uv venv
  1. To activate the virtual environment, use the command (the same as for venv).

source .venv/bin/activate

Note

Note that it is not necessary to activate the virtual environment to install packages with uv because each command starting with uv will automatically use the virtual environment if it exists.

  1. To install a package (e.g. pandas and streamlit), use the command:

uv add scikit-learn loguru

The packages will be both installed in the virtual environment and added to the pyproject.toml file. The uv.lock file will also be updated to reflect the installed packages and their versions.

And to add packages only for development (dev), you can create a group:

uv add --group dev pytest ruff

and let’s create a group for testing purpose

uv add --group test pytest

We can check the packages installed in the virtual environment with the command:

uv pip list

What is interesting with uv, is that it is possible to see the dependency tree of our installed packages. For this:

uv tree

In our case, you will see the following output:

keyrus-tuto-uv v0.1.0
├── pandas v2.3.0
│   ├── numpy v2.3.1
│   ├── python-dateutil v2.9.0.post0
│      └── six v1.17.0
│   ├── pytz v2025.2
│   └── tzdata v2025.2
└── streamlit v1.46.1
    ├── altair v5.5.0
       ├── jinja2 v3.1.6
          └── markupsafe v3.0.2
       ├── jsonschema v4.24.0
          ├── attrs v25.3.0
          ├── jsonschema-specifications v2025.4.1
             └── referencing v0.36.2
                 ├── attrs v25.3.0
                 ├── rpds-py v0.26.0
                 └── typing-extensions v4.14.1
          ├── referencing v0.36.2 (*)
          └── rpds-py v0.26.0
       ├── narwhals v1.45.0
       ├── packaging v25.0
       └── typing-extensions v4.14.1
    ├── blinker v1.9.0
    ├── cachetools v6.1.0
    ├── click v8.2.1
    ├── gitpython v3.1.44
       └── gitdb v4.0.12
           └── smmap v5.0.2
    ├── numpy v2.3.1
    ├── packaging v25.0
    ├── pandas v2.3.0 (*)
    ├── pillow v11.3.0
    ├── protobuf v6.31.1
    ├── pyarrow v20.0.0
    ├── pydeck v0.9.1
       ├── jinja2 v3.1.6 (*)
       └── numpy v2.3.1
    ├── requests v2.32.4
       ├── certifi v2025.6.15
       ├── charset-normalizer v3.4.2
       ├── idna v3.10
       └── urllib3 v2.5.0
    ├── tenacity v9.1.2
    ├── toml v0.10.2
    ├── tornado v6.5.1
    ├── typing-extensions v4.14.1
    └── watchdog v6.0.0
  1. To update the virtual environment with the latest versions of the packages, use the command:

uv sync

Note

A file uv.lock is created that contains the package on the system level

  1. To remove a package (e.g. pandas), use the command:

uv remove pandas
  1. To run a Python script (e.g. main.py), use the command:

Let’s create a main.py file in src/main.py:

nano src/main.py

and let’s add a simple script to train a ML Classifier on Iris Dataset

# main.py
from loguru import logger
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score


def main():
    logger.info("Loading the Iris dataset...")
    iris = load_iris()
    X, y = iris.data, iris.target

    logger.info("Splitting into train/test sets...")
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=42
    )

    logger.info("Initializing RandomForest model...")
    model = RandomForestClassifier(n_estimators=100, random_state=42)

    logger.info("Training the model...")
    model.fit(X_train, y_train)

    logger.info("Evaluating the model...")
    y_pred = model.predict(X_test)
    acc = accuracy_score(y_test, y_pred)

    logger.success(f"Test accuracy: {acc:.4f}")


if __name__ == "__main__":
    logger.add("train.log", rotation="1 MB", retention="7 days", level="INFO")
    logger.info("Starting training script.")
    main()
    logger.info("Training script finished.")

Then you can run the script:

uv run src/main.py
  1. To run a script with a specific package only for this run, use the command uv run –with:

For example, let’s remove loguru

uv remove loguru

if we run the code: uv run src/main.py, we will obtain an error:

ctdo@ZL008776:~/tmp/keyrus_tuto_uv git:(HEAD)> uv run src/main.py
Traceback (most recent call last):
File "/home/ctdo/tmp/keyrus_tuto_uv/src/main.py", line 2, in <module>
    from loguru import logger
ModuleNotFoundError: No module named 'loguru'

To run this script with loguru, you can use uv run –with

uv run --with loguru src/main.py

This should work again !!

Note

If you want to run with a group, for example, test, you can do

uv run --group pytest
  1. Finally, to delete the environment, just delete the .venv/ folder

rm -rf .venv
Remove-Item .\.venv\

Advanced Commands

  1. To commit properly, use commitizen:

Let’s add all our new file

git add .
uv run --with commitizen cz c

and let’s put the commit message

feat: initial commit
  1. Build a package

uv build

This command will create a dist directory containing the built package files.

  1. Publish a package

uv publish

This command will publish the package to the Python Package Index (PyPI) or any other specified repository.

  1. To bump the version of your package (tag):

let’s add this to your pyproject.toml

[tool.commitizen]
name = "cz_conventional_commits"
version_provider = "pep621"
tag_format = "v$version"
version_files = [
"pyproject.toml:^version =",
"VERSION"
]
bump_message = "chore(release): bump version $current_version$new_version [skip ci]"
update_changelog_on_bump = true
changelog_incremental = true
pre_bump_hooks = [
    "uv sync",
    "git add uv.lock"
]
changelog_file = "CHANGELOG.md"
changelog_title = "# Changelog \\n\\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\\n\\n## [Unreleased]\\n"
changelog_message_template = """
## {{version}} - {{date}}

{{#each commits}}
- {{message}}
{{/each}}

"""

and let’s create a VERSION file with version 0.1.0:

echo "0.1.0" >> VERSION

Then, you can use the command:

uv run --with commitizen cz bump
  1. To make a digest of your code (all your code in one .md file):

uv run --with gitingest gitingest .
  1. To run the ds-make squeleton from the DS team:

uv run --with cookiecutter cookiecutter git@gitlab.com:keyrus-data/infra/cookiecutter-data-squeleton.git --directory="data-science-v2"