Managing Python Projects With uv: An All-in-One Solution

Managing Python Projects With uv: An All-in-One Solution

by Leodanis Pozo Ramos Updated Reading time estimate 29m intermediate tools

The uv tool is a high-speed package and project manager for Python. It’s written in Rust and designed to streamline your workflow. It offers fast dependency installation and integrates various functionalities into a single tool.

With uv, you can install and manage multiple Python versions, create virtual environments, efficiently handle project dependencies, reproduce working environments, and even build and publish a project. These capabilities make uv an all-in-one tool for Python project management.

By the end of this tutorial, you’ll understand that:

  • uv is a Python package and project manager that integrates multiple functionalities into one tool, offering a comprehensive solution for managing Python projects.
  • uv is used for fast dependency installation, virtual environment management, Python version management, and project initialization, enhancing productivity and efficiency.
  • uv can build and publish Python packages to package repositories like PyPI, supporting a streamlined process from development to distribution.
  • uv automatically handles virtual environments, creating and managing them as needed to ensure clean and isolated project dependencies.

To dive deeper into managing your Python projects efficiently with uv, you should have a basic understanding of using Python virtual environments, setting up pyproject.toml files for projects, and building distributable packages for a project.

Take the Quiz: Test your knowledge with our interactive “Managing Python Projects With uv: An All-in-One Solution” quiz. You’ll receive a score upon completion to help you track your learning progress:


Interactive Quiz

Managing Python Projects With uv: An All-in-One Solution

In this quiz, you'll test your understanding of the uv tool, a high-speed package and project manager for Python.

Getting to Know uv for Python

Recently, a few exciting tools built with the Rust programming language have appeared in the Python tooling ecosystem. Ruff—a linter and code formatter for Python—is a well-known and popular example of one of these tools.

In this tutorial, you’ll explore another cool tool made with Rust for Python. You’ll get to know uv, an extremely fast Python package and project manager.

The main idea behind these tools is to accelerate your Python workflow by speeding up your project management actions. For example, Ruff is 10 to 100 times faster than linters like Flake8 and code formatters like Black. Similarly, for package installation, uv is 10 to 100 times faster than pip.

Pick a few packages below and start the race to compare how long uv and pip take to install them. The times are illustrative rather than a strict benchmark:

Interactive diagram — enable JavaScript to view.

Additionally, uv integrates into one tool most of the functionality provided by tools like pip, pip-tools, pipx, poetry, pyenv, twine, virtualenv, and more. Therefore, uv is an all-in-one solution.

Here’s a quick list of key uv features for managing your Python projects:

  • Fast dependency installation: Installs dependencies really fast, which is especially useful for large dependency trees.
  • Virtual environment management: Automatically creates and manages virtual environments.
  • Python version management: Allows the installation and management of multiple Python versions.
  • Project initialization: Scaffolds a full Python project, including the root directory, Git repository, virtual environment, pyproject.toml, README, and more.
  • Dependency management: Installs, updates, removes, and locks direct and transitive dependencies, which allows for environment reproducibility.
  • Package builds and publication management: Allows you to build and publish packages to package repositories like the Python Package Index (PyPI).
  • Developer tooling support: Installs and lets you run development tools, such as pytest, Black, and Ruff.

Apart from these features, uv is a standalone binary that allows for a smooth installation and quick upgrades. You don’t need to have Python installed on your system to install uv.

So, with this quick summary of uv and its main features, you’re ready to install this tool on your system. That’s what you’ll do in the following section. Additionally, you’ll learn how to update your uv installation.

Installing uv to Manage Python Projects

The first step in using any tool is to install it on your operating system. To install uv, you have several options. The quickest one would be to use the standalone installer. Another friendly option is to install uv from PyPI using other tools like pipx or pip.

In the official uv installation guide, you’ll find several other installation options. For example, you can use tools like Homebrew and Cargo, depending on your current platform and operating system. However, in this tutorial, you’ll only explore the standalone installer and the PyPI options.

Using the Standalone Installer

The uv project provides a standalone installer that you can use to download and install the tool in your system. Below are the relevant commands for the three main operating systems:

Language: Windows PowerShell
PS> powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
Language: Shell
$ curl -LsSf https://astral.sh/uv/install.sh | sh

If you don’t have curl installed on your system, then you can use wget as shown below:

Language: Shell
$ wget -qO- https://astral.sh/uv/install.sh | sh

These commands will download and install the latest binary of uv in your system. If you’d like to install a specific version of the tool instead of the latest, then you can add the version number to the download URL right after the uv/ part:

https://astral.sh/uv/0.6.12/install.ps1
https://astral.sh/uv/0.6.12/install.sh

With this addition to the download URL, you request the installation of uv version 0.6.12. Once you’ve downloaded and installed uv with the appropriate command, you can verify the installation by running the following command:

Language: Shell
$ uv --version
uv 0.6.12 (e4e03833f 2025-04-02)

Now you’re all set up with uv. In this case, you’re using version 0.6.12. Of course, your specific version may differ slightly depending on when you read this tutorial.

You can run the uv --help command at any time to display the help page, which provides detailed information about each command and option the tool offers. Go ahead and give it a try!

Installing From PyPI

You can also install uv from the Python Package Index (PyPI) using pipx. As with the standalone installer, the uv command-line-interface (CLI) will be available globally on your system.

To install uv using pipx, run the following command:

Language: Shell
$ pipx install uv

Note that for this command to work, you need pipx to be available in your environment. This involves an extra step. If you don’t want to install an additional app, then you can use pip, which is typically shipped by default with most Python distributions.

However, to have system-wide access to uv, you’ll have to install it in your Python system installation. This isn’t recommended because it could clutter your system with packages that you may not use.

Upgrading to the Latest uv Version

As you know, time flies. The uv project is currently under active development, which means that new versions are released regularly.

If you’ve installed uv using the standalone installer and would like to be up to date with the latest version, then you can run the following command:

Language: Shell
$ uv self update
info: Checking for updates...
success: Upgraded uv from v0.6.10 to v0.6.12!
⮑ https://github.com/astral-sh/uv/releases/tag/0.6.12

The uv self update command checks whether a new version is available. If that’s the case, the command downloads the new package and installs it for you.

If you’ve installed uv with pipx and want to upgrade it, then you can run the command below:

Language: Shell
$ pipx upgrade uv

This command allows you to upgrade your uv installation to the latest version if you used pipx.

Handling Python Projects With uv

Now comes the fun part—the part where you create and manage a Python project with uv. In this section, you’ll explore the uv features and commands that allow you to do this quickly and efficiently.

For this section and the rest of the tutorial, you’ll use a sample CLI application that retrieves cat information from the Cat API. The Cat API requires an API key, so get one from the Cat API website and store it in an environment variable called CAT_API_KEY before running the app.

Click the Show/Hide toggle below to reveal the app’s code:

Language: Python Filename: main.py
import argparse
import os
import sys

import requests

def get_breeds_info():
    response = requests.get(
        "https://api.thecatapi.com/v1/breeds",
        headers={"x-api-key": os.environ.get("CAT_API_KEY", "")},
    )
    response.raise_for_status()
    return response.json()

def find_breed_info(breed_name):
    json_response = get_breeds_info()
    for breed in json_response:
        if breed["name"] == breed_name:
            return breed
    return None

def display_breed_profile(breed):
    print(f"\n{breed['name']:-^30s}")
    print(f"Origin: {breed['origin']}")
    print(f"Temperament: {breed['temperament']}")
    print(f"Life Span: {breed['life_span']} years")
    print(f"Weight: {breed['weight']['imperial']} lbs")
    if breed.get("wikipedia_url"):
        print(f"\nLearn more: {breed['wikipedia_url']}")

def parse_args():
    parser = argparse.ArgumentParser(
        description="Get information about cat breeds"
    )
    parser.add_argument(
        "breed",
        help="Name of cat breed (e.g., 'Siamese')",
    )
    return parser.parse_args()

def main():
    args = parse_args()
    try:
        breed = find_breed_info(args.breed)
        if not breed:
            print("Breed not found. Try another breed name.")
            return 0
        display_breed_profile(breed)
    except Exception as e:
        print(f"Error: {e}")
        return 1
    return 0

if __name__ == "__main__":
    sys.exit(main())

Keep this code handy! You’ll use it to spice up your first Python project with uv. Are you ready?

Creating a Python Project

To create and initialize a Python project with uv, navigate to the directory where you want to store the project. Once there, you can run the following command to create and initialize the project:

Language: Shell
$ uv init --no-package rpcats

The --no-package flag tells uv to create a project without a build system. You’ll add one yourself later in this tutorial.

Note that the project name is up to you. In this example, you’ll use rpcats to denote that this is a Real Python project for retrieving and displaying information about cats.

The command above creates the following directory structure under the rpcats/ folder:

rpcats/
│
├── .git/
│
├── .gitignore
├── .python-version
├── README.md
├── main.py
└── pyproject.toml

This is cool! First, you have the .git/ directory, which is the Git repository for your project. The .gitignore file allows you to define the files and folders you want to skip in your version control workflow. By default, uv automatically populates this file with sensible entries for most Python projects, ensuring that unwanted files are excluded from version control.

The .python-version file contains the default Python version for the current project. This file tells uv which Python version to use when creating a dedicated virtual environment for the project. Next, you have an empty README.md file that you can use to provide basic documentation for your project.

The main.py file is a placeholder Python file that initially contains the following code:

Language: Python
def main():
    print("Hello from rpcats!")

if __name__ == "__main__":
    main()

In this case, you have a main() function that prints a message to the screen. At the bottom of the file, you have the well-known if __name__ == "__main__" idiom, which is common practice in executable files like this one.

Finally, you have the pyproject.toml file with the following initial content:

Language: TOML
[project]
name = "rpcats"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13"
dependencies = []

This file contains basic configuration key-value pairs under the [project] table. You can update the value of any key to meet your requirements. For example, you can change the description to something like the following:

Language: TOML
[project]
name = "rpcats"
version = "0.1.0"
description = "Display cat information for the specified breed."
readme = "README.md"
requires-python = ">=3.13"
dependencies = []

Now, your project has a concrete description. You could also change the project’s version, the required Python version, and so on.

That’s it! You’ve created your first project with uv. Now you can run the project’s entry-point script, main.py, to check that everything’s working correctly.

Running the Project’s Entry-Point Script

Once you’ve created the project, you can use uv to run the entry-point script, which is the main.py file by default. To run the script, go ahead and execute the following command:

Language: Shell
$ uv run main.py
Using CPython 3.13.2
Creating virtual environment at: .venv
Hello from rpcats!

The first time you run this command, it’ll display the Python version you’re currently using for the project. It’ll also inform you that uv has created a dedicated Python virtual environment for the project. The final output line shows the message that results from calling the main() function defined in main.py.

If you check the content of your project’s root directory again, then you’ll find a .venv folder containing the dedicated virtual environment. You’ll also find a new file called uv.lock, which is a cross-platform lockfile that contains information about your project’s dependencies. This file ensures that other developers can quickly and exactly reproduce your working environment. This enhances team collaboration and code contributions.

Unlike pyproject.toml, which usually specifies the direct dependencies of your project, the uv.lock file contains the exact versions of any dependency installed in the project environment.

The uv.lock file uses the TOML format. You should add this file to version control. However, you shouldn’t edit this file manually. Instead, you can let uv take care of it. You’ll learn more about this file in a moment.

Using uv for Dependency Management

When it comes to managing your project’s dependencies, uv makes your life easier with a clean workflow. This workflow allows you to lock your project’s dependencies so that other developers can reproduce your environment exactly and contribute to your code without much setup effort.

In the following sections, you’ll learn the basic uv commands that you should use to install, update, and remove external packages and libraries. You’ll also learn how these commands create and update the corresponding configuration files to ensure reproducibility.

Adding and Installing Dependencies

Now, go back to the beginning of the Handling Python Projects With uv section, expand the collapsible block, and copy the code for the sample CLI app. Paste the code into the main.py file in your project’s root folder.

Once you have the code in place, you might be tempted to run the app with a command like the following:

Language: Shell
$ uv run main.py Persian
Traceback (most recent call last):
    ...
ModuleNotFoundError: No module named 'requests'

The app fails with a ModuleNotFoundError exception. Why? Because you haven’t installed the required dependencies in your working environment.

In your cat app example, you need the Requests library, which is a popular library for making HTTP requests in Python. You use this library to retrieve cat information from the Cat API.

Go ahead and run the following command to make uv add requests:

Language: Shell
$ uv add requests

This command lets you add the Requests library to your project’s dependencies. It also installs the library in your project’s virtual environment. Once it finishes running, go ahead and check the content of your pyproject.toml file:

Language: TOML
[project]
name = "rpcats"
version = "0.1.0"
description = "Display cat information for the specified breed."
readme = "README.md"
requires-python = ">=3.13"
dependencies = [
    "requests>=2.32.3",
]

Notice how uv add automatically updates your list of dependencies by adding requests to the pyproject.toml file. In this case, the installed version should be greater or equal to 2.32.3.

Internally, uv add also updates the uv.lock file with version information for the following:

  • Direct dependencies: Packages that your project depends on directly. For example, your cat app depends on requests.
  • Transitive dependencies: Packages that support your project’s direct dependencies. For example, the Requests library depends on urllib3, and you get it installed as a transitive dependency.

You don’t have to worry about the uv.lock content and shouldn’t edit it yourself. Instead, let uv be in charge of it. However, you must version-control it.

As you can conclude, uv add does the dependency management work for you. It installs the dependencies, edits the pyproject.toml file if necessary, and keeps the uv.lock file up-to-date. This ensures that other developers can reproduce your working environment.

Would you like to give your cat app another try? Go ahead and run it:

Language: Shell
$ uv run main.py Persian

-----------Persian------------
Origin: Iran (Persia)
Temperament: Affectionate, Loyal, Sedate, Quiet
Life Span: 14 - 15 years
Weight: 9 - 14 lbs

Learn more: https://en.wikipedia.org/wiki/Persian_(cat)

Now, your cat app is working correctly. It makes API requests, processes the responses, and displays information for the target breed of cat.

Upgrading and Removing Dependencies

You can also use uv to update and remove dependencies. For example, say that some time has passed since you released the latest version of your rpcats app, and you’d like to continue the development by adding new features. However, you realize a new version is available for the Requests library.

In this situation, you can use the following command to upgrade requests:

Language: Shell
$ uv lock --upgrade-package requests

This command upgrades requests and updates its version information in the uv.lock file. This action allows you to work with the library’s latest version and ensures that other developers can do the same.

If you decide at any point that your project doesn’t need a dependency anymore, then you can remove it with the following command: