Mastering Python Project Management with uv: Part 2 — Deep Dives and Advanced Use
🛠 Updated on 23rd Feb 2025
This article has been revised based on valuable feedback from readers, thank you all!
Welcome back! If you haven’t checked out Part 1, we explored how uv
simplifies managing dependencies, creating virtual environments, handling Python versions, and utilizing inline metadata. In this installment, we'll delve deeper into uv
's advanced features, aligning with the latest PEP standards and uv
documentation, to elevate your Python development experience.— -
1. Managing Dependencies with pyproject.toml
In Part 1, we introduced adding dependencies using uv
. Let's expand on how uv
integrates seamlessly with pyproject.toml
, the standardized configuration file for Python projects.
Adding Dependencies with uv add
When you add a dependency using uv add
, it updates your pyproject.toml
file accordingly:
uv add fastapi
This command modifies your pyproject.toml
to include:
[project]
name = "your_project_name"
version = "0.1.0"
dependencies = [
"fastapi>=0.68,<1.0",
]
This makes your dependencies explicit and your project easily shareable with others. Whenever you or someone else clones your project, simply running uv
will install the same packages listed.
Version Constraints: >=
vs. ==
It’s advisable to use version ranges (e.g., >=0.68,<1.0
) rather than strict pinning (==0.68
). This approach allows for compatibility with newer, non-breaking versions, ensuring your project remains up-to-date without unexpected disruptions. Strict pinning can lead to dependency conflicts and hinder the integration of security patches or performance improvements.
Optional Dependencies and Dependency Groups
uv
supports the standardized optional-dependencies
and dependency-groups
as per the latest PEPs:
[project.optional-dependencies]
dev = ["pytest>=6.0", "black"]
[tool.uv.dependency-groups.docs]
dependencies = ["sphinx>=4.0"]
optional = true
To install these groups, use:
uv add --group dev <DEV_PACKAGE_01> <DEV_PACKAGE_02>
uv add --group docs <DOCS_PACKAGE_01> <DOCS_PACKAGE_02> <DOCS_PACKAGE_03>
for development dependencies and documentation dependencies
— -
2. Locking Dependencies with the uv.lock
File
Whenever you add or update dependencies using uv
, it doesn’t just modify your pyproject.toml
file. uv
also creates a uv.lock
file. Why is this important?
- Precise Versioning: The uv.lock
file locks in the exact versions of all dependencies and their transitive dependencies (packages that your dependencies rely on).
- Reproducible Environments: Whether it’s you coming back to a project after a break or a colleague cloning your repo, running uv
will install the exact versions specified in the `uv.lock` file.
The result? A consistent, reliable environment that eliminates the “it works on my machine” problem.
— -
3. Managing Tools: Global vs. Project-Specific
In Part 1, we discussed how uv
makes it easy to install CLI tools. Now, let’s break down how uv
distinguishes between global and project-specific tools.
Installing Global Tools
Installing a tool globally with uv
is simple:
uv tool install black
This makes black
available across all your projects but keeps it in its isolated virtual environment, avoiding system-wide conflicts.
Project-Specific Tools
If you need a tool for a specific project, add it directly as a dependency:
uv add --group dev ruff
This keeps the tool local to your project and listed in your pyproject.toml
. Your other projects remain unaffected, allowing for isolated development environments.
Running Tools Ephemerally with uvx
For quick, one-off tool usage without permanently installing it, uvx
is your friend:
uvx black my_script.py
This runs black
within a temporary virtual environment and then cleans up afterward.
— -
4. Creating & Using Virtual Environments the Right Way
Making Virtual Environments Easy
In Part 1, we explained how `uv` defaults to using virtual environments for all package installations. Here’s a quick recap:
uv venv
This command creates a `.venv` directory in your project. If you want to use a custom directory or Python version:
uv venv my_venv - python 3.11
You can then activate your virtual environment as you normally would:
`$ .venv/Scripts/activate`
Automatic Environment Detection
Whenever you work on a project managed by `uv`, the tool will automatically detect and use the appropriate virtual environment without any additional setup. No need to worry about manually activating or deactivating environments.
— -
5. `uv` and Existing Environments
Already using another environment manager like conda
? No problem. uv
is built to play nicely with external virtual environments.
Automatic Environment Detection & Integration
When you use uv pip install
or uv add
, uv
searches for existing virtual environments:
1. Activated environments (e.g., `VIRTUAL_ENV` or `CONDA_PREFIX`).
2. A `.venv` directory in your current project.
If `uv` doesn’t find a virtual environment, it will prompt you to create one to keep your environment clean and isolated.
— -
6. Using Alternative Package Indexes and Authentication
While uv
defaults to the official Python Package Index (PyPI), it supports alternative indexes, which often require authentication.
Configuring Alternative Indexes
Set an alternative index URL:
export UV_INDEX=https://example.com/simple
Authentication Using Environment Variables
For indexes requiring authentication, provide credentials via environment variables. For instance, with Azure Artifacts:
export UV_INDEX=https://username:$ADO_PAT@pkgs.dev.azure.com/organization/project/_packaging/feedName/pypi/simple/
Replace $ADO_PAT
with your Personal Access Token. Refer to the official uv
documentation for detailed instructions on various services.
7. Permanent Configuration
To avoid setting environment variables repeatedly, configure uv
persistently.
Within a project, add the index URL to pyproject.toml
:
[tool.uv]
index = [
{ url = "https://example.com/simple", default = true }
]
Alternatively, use uv.toml
(preferred for non-Python-specific configurations):
[[index]]
url = "https://example.com/simple"
default = true
Precedence Order:
uv.toml
overridespyproject.toml
if both exist in the same directory.- Project-level settings override user-level settings, which override system-level settings.
- Environment variables take priority over all configuration files.
- Use
uv --no-config
to temporarily ignore all configurations.
8. Types of Projects and Build System Options
uv
supports multiple project types, each with different build system configurations. Selecting the right configuration depends on your project's packaging and distribution needs.
Project Types Supported in uv
:
What’s Next?
✔ New to uv
? Start with Part 1.
✔ Want more details? Check out the official uv
documentation.
💬 Have you tried uv
yet? Share your experience and questions in the comments! 🐍✨