Motivation and Methods for Migrating Existing Python Projects to uv

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 Target | Management Method |
Python Version | pyenv |
Dependencies | pip + requirements.txt |
Virtual Environment | pyenv-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.
Issue | Cause | Countermeasures |
Dependency resolution often fails during library installation, forcing manual investigation and resolution | pip's dependency resolution algorithm is relatively naive[^1], making it impossible to build completely consistent dependencies overall | Use 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 environment | pyenv-virtualenv and venv virtual environments require manual activation | Use 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/unclear | Everything tends to be put in a single requirements.txt | Use 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
.
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]: 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)
Subscribe to my newsletter
Read articles from teihenn directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
