Motivation and Methods for Migrating Existing Python Projects to uv

teihennteihenn
6 min read

Article Overview

This article describes the motivation and methods for migrating existing Python projects to uv management. The detailed usage of uv is not covered in this article (a separate article is planned).

Note: The content of this article is based on information from uv v0.6.13. Please refer to the official documentation for the latest information.

Target Audience

  • People who are interested in uv but hesitate to migrate

    • → I recommend creating a branch and trying migration to experience the usability.

Common Existing Configuration of Python Projects

We assume the following common configuration for existing Python projects:

Management TargetManagement Method
Python Versionpyenv
Dependenciespip + requirements.txt
Virtual Environmentpyenv-virtualenv or venv

This is a common configuration when not using modern package managers.

Issues with the Above Configuration Management

When managing with the above configuration, you encounter the following issues. I've also included the causes and possible countermeasures.

IssueCauseCountermeasures
Dependency resolution often fails during library installation, forcing manual investigation and resolutionpip's dependency resolution algorithm is relatively naive[^1], making it impossible to build completely consistent dependencies overallUse tools with more advanced dependency resolution algorithms / Use tools with clear error messages
Complete environment reproduction is difficult because transitive dependencies cannot be managed (especially in machine learning, complete environment reproduction is desirable)requirements.txt only manages directly dependent libraries[^2]Use tools with lock file mechanisms / Containerize each application
When running locally, forgetting to activate the virtual environment and running pip install installs globally, easily contaminating the global environmentpyenv-virtualenv and venv virtual environments require manual activationUse tools that can use virtual environments without being conscious of it
Production dependencies and development dependencies tend to be mixed together, making it difficult to configure minimal environments, properly separate CI pipelines, and making them fragile/unclearEverything tends to be put in a single requirements.txtUse tools that can manage them separately

Brief Explanation of uv

Using uv, a Python development environment management tool, can solve the above issues.

https://docs.astral.sh/uv/ You can understand the overview of uv by looking at the Introduction page above.

However, let me briefly list the features here:

  • Can manage Python versions, dependencies, and virtual environments with uv alone

  • Has an intuitive CLI (similar to poetry)

  • Operates extremely fast (uv is written in Rust and solves dependency resolution problems quickly)

  • Has cross-platform lock files

  • Has advanced dependency resolution algorithms

  • Supports macOS, Linux, Windows

For Rust users, it's a "Cargo in Python" philosophy tool, so the user experience is close to Cargo and very user-friendly.

I compared it with Poetry and PDM, but I think uv is the way to go now as the latest tool. uv has the advantage of being able to manage Python versions, making other tools unnecessary, and operating surprisingly fast compared to them. Although uv is in version 0.x, it is already considered Stable and core functions are already stable, so I think it's ready for production use. In the latest Technology Radar (April 2025, Vol32), it received the highest rank of Adopt (strongly recommended for adoption if it fits the project).

Preparation: Installing uv

https://docs.astral.sh/uv/getting-started/installation/

Please install the standalone version from the above (installing with pip install ties it to the Python environment, so if you can install the standalone version, that's better).

$ curl -LsSf https://astral.sh/uv/install.sh | sh
downloading uv 0.6.13 x86_64-unknown-linux-gnu
no checksums to verify
installing to /home/ec2-user/.local/bin
  uv
  uvx
everything's installed!

Migration Steps for Existing Projects to uv Management

We assume migrating a repository called my-python-repo managed with the above configuration to uv management.

https://docs.astral.sh/uv/guides/projects/

1. uv init & Setting Python Version

By running the following in the project root, we initialize it as a uv-managed project using Python 3.13.2.

$ uv init -p 3.13.2
Initialized project `my-python-repo`

This will create main.py, pyproject.toml, .python-version, and README.md.

main.py

def main():
    print("Hello from my-python-repo!")

if __name__ == "__main__":
    main()

pyproject.toml

[project]
name = "my-python-repo"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.13.2"
dependencies = []

.python-version

3.13.2

When you run uv run main.py, a virtual environment (.venv directory[^3]) and uv.lock are created.

$ uv run main.py
Using CPython 3.13.2
Creating virtual environment at: .venv
Hello from my-python-repo!

main.py is not needed, so you can delete it. Also, you should update the meta information such as the project name in pyproject.toml as needed.

2. Adding Dependencies to pyproject.toml

https://docs.astral.sh/uv/guides/projects/#managing-dependencies

Since uv manages dependencies in pyproject.toml, we migrate the current requirements.txt content there.

Migration from requirements.txt can be done with uv add -r, but first, if requirements.txt contains both production-required libraries and development-only libraries, separate them into two.

Example:

requirements-new-lib.txt

pyarrow==19.0.1
pandas==2.2.3
lightgbm==4.6.0
...

requirements-new-dev.txt

moto==5.1.3
my-python-test-libs==1.4.2
...

Using the above, add dependencies to pyproject.toml with the following commands:

$ uv add -r requirements-new-lib.txt
$ uv add -r requirements-new-dev.txt --dev

If there are conflicting dependencies that cannot be resolved, you will get an error like the following. For example, if there is a repository called my-python-test-libs (fictitious) like in the above example, and it requires pandas < 2.0.0, it will conflict with pandas==2.2.3. If my-python-test-libs is under your management, it's desirable to test that pandas 2.2.3 can be used without problems and update the dependency.

  Updated ssh://git@xxx.org/my-techorg/my-python-test-libs.git (6gp94f50b4dc39402f612f9df7bdd6aabbc53471)
  × No solution found when resolving dependencies:
  ╰─▶ Because your project depends on pandas==2.2.3 and my-python-test-libs==1.4.2 depends on pandas>=1.3.0,<2.0.0, we can
      conclude that your project and my-python-test-libs==1.4.2 are incompatible.
      And because only my-python-test-libs==1.4.2 is available and my-python-repo:dev depends on my-python-test-libs, we can conclude that
      your project and my-python-repo:dev are incompatible.
      And because your project requires your project and my-python-repo:dev, we can conclude that your project's
      requirements are unsatisfiable.
  help: If you want to add the package regardless of the failed resolution, provide the `--frozen` flag to skip locking
        and syncing.

However, using --frozen allows you to force addition without recording to the lock file^4. If you really want to add it as is, you should understand that runtime errors may occur due to how my-python-test-libs uses pandas.

3. Testing

Confirm that it works correctly after uv migration by running the application or executing automated tests. For example, to run a uv-managed Python application, execute uv run as follows:

$ uv run my-python-repo-main.py

4. Clean Up Unnecessary Items

  • Delete unnecessary requirements.txt

  • If there are shell scripts executing Python scripts, rewrite them to go through uv and other cleanup tasks to complete the migration.

Summary

The migration is just this much, and I think it can be done easily without any particular difficulty. The development experience with uv is very good, so I recommend it.

References

  1. https://docs.astral.sh/uv/

[^1]: pip seems to be getting stronger

[^2]: You can create a lock file with pip freeze > requirements.lock, but you probably don't do that...

[^3]: uv also uses .venv as a virtual environment (but it's designed to be usable without being conscious of it)

[^4]: https://docs.astral.sh/uv/reference/cli/#uv-add

0
Subscribe to my newsletter

Read articles from teihenn directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

teihenn
teihenn