UV: The Ultra-Fast Python Package Manager

A Comprehensive Beginner’s Guide to Modern Python Development

Author

Md Rasheduzzaman

Last updated

September 20, 2025

Introduction

Welcome to the world of UV - the ultra-fast Python package manager that’s revolutionizing how we work with Python projects! If you’ve ever been frustrated by slow pip installations, complex virtual environment setups, or dependency conflicts, UV is here to solve all those problems.

What is UV?

UV is a modern, extremely fast Python package and project manager written in Rust. It’s designed to replace multiple tools you might currently use:

  • pip (package installation)
  • pip-tools (dependency management)
  • pipx (tool installation)
  • poetry (project management)
  • pyenv (Python version management)
  • virtualenv (virtual environments)

Why Choose UV?

Key Benefits
  • ⚡ Lightning Fast: 10-100x faster than pip
  • 🔧 All-in-One: Replaces multiple tools
  • 🔒 Reliable: Advanced dependency resolution
  • 🐍 Python Management: Built-in Python version handling
  • 📦 Modern: Lock files for reproducible builds
  • 🌐 Cross-Platform: Works on Windows, macOS, and Linux

Installation

Installing UV

The installation process is straightforward and works across all major platforms.

For macOS and Linux:

curl -LsSf https://astral.sh/uv/install.sh | sh

For Windows (PowerShell):

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Post-Installation Setup

After installation, you need to refresh your terminal environment:

Option 1: Restart your terminal (Recommended)

Option 2: Reload your shell configuration:

source $HOME/.local/bin/env

Verify Installation

Check if UV is installed correctly:

uv --version

You should see output similar to:

uv 0.4.18 (Rust 1.81.0)

Python Version Management

One of UV’s most powerful features is its ability to manage Python versions without needing additional tools like pyenv.

List Available Python Versions

uv python list

This command shows: - Python versions already installed on your system - Python versions available for download

Example output:

 * python3.13 -> /usr/bin/python3.13 (system)
 * python3.12 -> /usr/bin/python3.12 (system)
   python3.11 -> download
   python3.10 -> download

Install Specific Python Versions

# Install Python 3.13
uv python install 3.13

# Install Python 3.12
uv python install 3.12

# Install Python 3.11
uv python install 3.11

UV handles the download and installation automatically!

Check Python Version Availability

uv python find 3.13

This returns the path to Python 3.13 if available, or indicates it can be downloaded.

Basic Script Execution

Let’s start with a simple example to see UV in action.

Your First UV Script

Create a file called basic_example.py:

#!/usr/bin/env python3
"""
Basic UV Demo Script
Run with: uv run basic_example.py
"""
def main():
    print("Welcome to UV Tutorial!")
    print("Hello from UV - Ultra Fast Python Package Manager!")
    
    # Show Python version and location
    import sys
    print(f"\nPython Version: {sys.version}")
    print(f"Python Executable: {sys.executable}")
    print(f"Platform: {sys.platform}")
    
    # Show some basic info
    import os
    print(f"\nCurrent working directory: {os.getcwd()}")
    
    print("\n✅ UV is working perfectly!")
    print("Now let's explore more advanced features!")

if __name__ == "__main__":
    main()

Run the Script

# Run with default Python
uv run basic_example.py

# Run with specific Python version
uv run --python 3.13 basic_example.py

Temporary Dependencies

Here’s where UV shines! You can run scripts with dependencies without installing them permanently:

# Run a script that needs the 'rich' library
uv run --with rich script_name.py

# Multiple temporary dependencies
uv run --with rich --with requests --python 3.13 script_name.py

Let’s create a more exciting example:

#!/usr/bin/env python3
"""
Rich Library Demo
Run with: uv run --with rich rich_demo.py
"""

from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.text import Text
from rich.progress import track
import time

def main():
    console = Console()
    
    # Welcome message
    welcome_text = Text("🚀 UV Package Manager Demo", style="bold blue")
    panel = Panel(welcome_text, title="Python Tutorial", border_style="green")
    console.print(panel)
    
    # Create a comparison table
    console.print("\n[bold yellow]Package Manager Comparison:[/bold yellow]")
    
    table = Table(title="pip vs UV Comparison")
    table.add_column("Feature", style="cyan", no_wrap=True)
    table.add_column("pip", style="red")
    table.add_column("UV", style="green")
    
    comparisons = [
        ("Installation Speed", "🐌 Slow (10-60s)", "⚡ Lightning Fast (<2s)"),
        ("Dependency Resolution", "❌ Basic", "✅ Advanced SAT solver"),
        ("Virtual Environment", "❌ Manual setup", "✅ Automatic"),
        ("Lock Files", "❌ No lock files", "✅ uv.lock for reproducibility"),
        ("Python Version Mgmt", "❌ Needs pyenv", "✅ Built-in"),
        ("Parallel Downloads", "❌ Sequential", "✅ Parallel processing"),
        ("Caching", "❌ Limited", "✅ Smart global cache")
    ]
    
    # Add rows with animation
    for feature, pip_val, uv_val in track(comparisons, description="Loading comparison..."):
        table.add_row(feature, pip_val, uv_val)
        time.sleep(0.3)
    
    console.print(table)
    
    # Summary
    console.print("\n[bold green]🎉 UV makes Python development 10x easier and faster![/bold green]")
    console.print("[italic]This rich formatting was installed temporarily with --with flag![/italic]")

if __name__ == "__main__":
    main()

Run this with:

uv run --with rich rich_demo.py

Project Management

Creating a New Project

UV makes it incredibly easy to start new Python projects:

# Create a new directory and initialize a UV project
mkdir my_awesome_project && cd my_awesome_project
uv init

This creates: - pyproject.toml - Project configuration file - src/ - Source code directory structure - main.py - A starter Python file - Virtual environment management (automatic)

Project Structure

After running uv init, you’ll see:

my_awesome_project/
├── pyproject.toml
├── README.md
├── src/
│   └── my_awesome_project/
│       ├── __init__.py
│       └── py.typed
└── main.py

Script-Based Projects

For simple scripts, you can create a script-based project:

uv init --script main.py --python 3.13

This creates a minimal configuration for single-script projects.

Dependency Management

Adding Dependencies

Adding packages to your project is straightforward:

# Add regular dependencies
uv add requests
uv add beautifulsoup4
uv add rich

# Add with version constraints
uv add "django>=4.0,<5.0"
uv add "requests>=2.25.0"

Development Dependencies

Separate your development tools from production dependencies:

# Add development dependencies
uv add --dev pytest
uv add --dev black
uv add --dev mypy
uv add --dev ruff

Optional Dependencies

Create optional dependency groups for different use cases:

# Add optional dependencies
uv add --optional web fastapi uvicorn
uv add --optional data pandas numpy matplotlib

# Install with optional groups
uv sync --extra web
uv sync --extra data
uv sync --extra web,data

Synchronizing Environment

Keep your environment in sync with your project configuration:

# Install all dependencies
uv sync

# Sync with fresh downloads (ignore cache)
uv sync --refresh

# Show what would be installed (dry run)
uv sync --dry-run

Understanding pyproject.toml

The pyproject.toml file is the heart of your UV project. It’s written in TOML (Tom’s Obvious Minimal Language), which is designed to be human-readable and easy to parse.

Key sections in pyproject.toml:

[project]
name = "my-awesome-project"
version = "0.1.0"
description = "A sample project using UV"
requires-python = ">=3.8"
dependencies = [
    "requests>=2.25.0",
    "rich>=10.0.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=6.0",
    "black>=21.0",
    "mypy>=0.800",
]
web = [
    "fastapi>=0.68.0",
    "uvicorn>=0.15.0",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.uv]
dev-dependencies = [
    "pytest>=6.0",
    "black>=21.0",
]

What each section means:

  • [project]: Basic project metadata and core dependencies
  • [project.optional-dependencies]: Optional groups you can install selectively
  • [build-system]: How to build/package your project
  • [tool.uv]: UV-specific configuration

Lock Files: Ensuring Reproducibility

UV automatically generates uv.lock files that contain the exact versions of all dependencies (including transitive dependencies) that work together:

# Update lock file with latest compatible versions
uv lock

# Install exactly what's in the lock file
uv sync --frozen

Why lock files matter:

  • Reproducibility: Everyone on your team gets identical environments
  • Security: Prevents supply chain attacks through version pinning
  • Debugging: Easier to track down issues when versions are consistent
  • CI/CD: Ensures production matches development

Lock file example snippet:

[[package]]
name = "requests"
version = "2.31.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
    { name = "certifi" },
    { name = "charset-normalizer" },
    { name = "idna" },
    { name = "urllib3" },
]

Synchronization: Keeping Everything in Sync

The uv sync command is powerful - it ensures your environment exactly matches your project configuration:

# Standard sync - installs missing, updates changed
uv sync

# Sync with specific extras
uv sync --extra dev
uv sync --extra web,dev

# Frozen sync - only installs what's in uv.lock (production mode)
uv sync --frozen

# Refresh sync - ignores cache, downloads fresh
uv sync --refresh

# Dry run - see what would happen without doing it
uv sync --dry-run

What sync does:

  1. Reads pyproject.toml for project requirements
  2. Compares with current environment
  3. Installs missing packages
  4. Removes packages not in configuration
  5. Updates packages to match version constraints
  6. Creates/updates uv.lock if needed

Best practices:

  • Run uv sync after pulling changes from git
  • Use uv sync --frozen in production/CI
  • Commit both pyproject.toml and uv.lock to version control

Real-World Example: Student Grade Calculator

Let’s build a practical classroom example - a student grade calculator that reads data from CSV files and generates reports.

Project Setup

mkdir grade_calculator && cd grade_calculator
uv init
uv add pandas rich

Create Sample Data

First, create a sample CSV file students.csv:

name,math,science,english,history
Alice Johnson,95,88,92,85
Bob Smith,78,91,85,88
Carol Davis,88,95,90,92
David Wilson,92,85,88,90
Eva Brown,85,92,95,88
Frank Miller,90,88,85,92
Grace Lee,88,90,92,85
Henry Clark,92,88,90,88
Ivy Taylor,85,95,88,90
Jack Anderson,90,85,92,88

The Grade Calculator Code

Create grade_calculator.py:

#!/usr/bin/env python3
"""
Student Grade Calculator - Real world UV example
Dependencies: pandas, rich

Setup commands:
uv init
uv add pandas rich
uv run grade_calculator.py
"""

import pandas as pd
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich.progress import track
from rich.text import Text
import statistics
from pathlib import Path

def create_sample_data():
    """Create sample student data if CSV doesn't exist"""
    if not Path("students.csv").exists():
        data = {
            'name': ['Alice Johnson', 'Bob Smith', 'Carol Davis', 'David Wilson', 
                    'Eva Brown', 'Frank Miller', 'Grace Lee', 'Henry Clark', 
                    'Ivy Taylor', 'Jack Anderson'],
            'math': [95, 78, 88, 92, 85, 90, 88, 92, 85, 90],
            'science': [88, 91, 95, 85, 92, 88, 90, 88, 95, 85],
            'english': [92, 85, 90, 88, 95, 85, 92, 90, 88, 92],
            'history': [85, 88, 92, 90, 88, 92, 85, 88, 90, 88]
        }
        df = pd.DataFrame(data)
        df.to_csv('students.csv', index=False)
        return df
    return None
#or load the file if you already have it, like below
def load_student_data():
    """Load student data from CSV file"""
    try:
        df = pd.read_csv('students.csv')
        return df
    except FileNotFoundError:
        console = Console()
        console.print("[red]Error: students.csv not found. Creating sample data...[/red]")
        return create_sample_data()

def calculate_grades(df):
    """Calculate grades and statistics"""
    # Calculate average for each student
    subject_columns = ['math', 'science', 'english', 'history']
    df['average'] = df[subject_columns].mean(axis=1)
    
    # Assign letter grades
    def get_letter_grade(avg):
        if avg >= 90: return 'A'
        elif avg >= 80: return 'B'
        elif avg >= 70: return 'C'
        elif avg >= 60: return 'D'
        else: return 'F'
    
    df['letter_grade'] = df['average'].apply(get_letter_grade)
    
    return df

def display_student_report(df, console):
    """Display individual student grades"""
    table = Table(
        title="📚 Student Grade Report",
        show_header=True,
        header_style="bold blue",
        border_style="green"
    )
    
    table.add_column("Student", style="cyan", width=15)
    table.add_column("Math", justify="center", style="yellow")
    table.add_column("Science", justify="center", style="yellow")
    table.add_column("English", justify="center", style="yellow")
    table.add_column("History", justify="center", style="yellow")
    table.add_column("Average", justify="center", style="magenta")
    table.add_column("Grade", justify="center", style="bold green")
    
    for _, student in track(df.iterrows(), description="Processing grades...", total=len(df)):
        # Color code the letter grade
        grade_color = {
            'A': 'bold green',
            'B': 'green', 
            'C': 'yellow',
            'D': 'orange1',
            'F': 'red'
        }.get(student['letter_grade'], 'white')
        
        table.add_row(
            student['name'],
            str(int(student['math'])),
            str(int(student['science'])),
            str(int(student['english'])),
            str(int(student['history'])),
            f"{student['average']:.1f}",
            f"[{grade_color}]{student['letter_grade']}[/{grade_color}]"
        )
    
    console.print(table)

def display_class_statistics(df, console):
    """Display class-wide statistics"""
    subject_columns = ['math', 'science', 'english', 'history']
    
    # Calculate statistics
    stats_table = Table(
        title="📊 Class Statistics",
        show_header=True,
        header_style="bold magenta",
        border_style="blue"
    )
    
    stats_table.add_column("Subject", style="cyan")
    stats_table.add_column("Average", justify="center")
    stats_table.add_column("Highest", justify="center", style="green")
    stats_table.add_column("Lowest", justify="center", style="red")
    stats_table.add_column("Std Dev", justify="center")
    
    for subject in subject_columns:
        avg = df[subject].mean()
        highest = df[subject].max()
        lowest = df[subject].min()
        std_dev = df[subject].std()
        
        stats_table.add_row(
            subject.title(),
            f"{avg:.1f}",
            str(int(highest)),
            str(int(lowest)),
            f"{std_dev:.1f}"
        )
    
    console.print(stats_table)
    
    # Grade distribution
    grade_dist = df['letter_grade'].value_counts().sort_index()
    
    console.print("\n[bold yellow]📈 Grade Distribution:[/bold yellow]")
    for grade, count in grade_dist.items():
        percentage = (count / len(df)) * 100
        console.print(f"  {grade}: {count} students ({percentage:.1f}%)")

def generate_report_file(df):
    """Generate a detailed report file"""
    report_content = []
    report_content.append("STUDENT GRADE REPORT")
    report_content.append("=" * 50)
    report_content.append("")
    
    for _, student in df.iterrows():
        report_content.append(f"Student: {student['name']}")
        report_content.append(f"  Math: {student['math']}")
        report_content.append(f"  Science: {student['science']}")
        report_content.append(f"  English: {student['english']}")
        report_content.append(f"  History: {student['history']}")
        report_content.append(f"  Average: {student['average']:.1f}")
        report_content.append(f"  Letter Grade: {student['letter_grade']}")
        report_content.append("")
    
    # Class statistics
    report_content.append("CLASS STATISTICS")
    report_content.append("-" * 30)
    subject_columns = ['math', 'science', 'english', 'history']
    for subject in subject_columns:
        avg = df[subject].mean()
        report_content.append(f"{subject.title()} Average: {avg:.1f}")
    
    report_content.append(f"\nClass Average: {df['average'].mean():.1f}")
    
    # Save to file
    with open('grade_report.txt', 'w') as f:
        f.write('\n'.join(report_content))

def main():
    console = Console()
    
    # Header
    header_panel = Panel(
        "[bold blue]🎓 Student Grade Calculator[/bold blue]",
        subtitle="Powered by UV Package Manager",
        border_style="green"
    )
    console.print(header_panel)
    
    # Load data
    with console.status("[bold green]Loading student data...", spinner="dots"):
        df = load_student_data()
        if df is None:
            console.print("[red]Failed to load data![/red]")
            return
    
    console.print(f"[green]✅[/green] Loaded data for {len(df)} students")
    
    # Calculate grades
    with console.status("[bold blue]Calculating grades...", spinner="bouncingBall"):
        df = calculate_grades(df)
    
    console.print("[green]✅[/green] Grades calculated successfully!")
    
    # Display results
    display_student_report(df, console)
    console.print("")
    display_class_statistics(df, console)
    
    # Generate report file
    generate_report_file(df)
    console.print(f"\n[bold green]💾 Detailed report saved to grade_report.txt[/bold green]")
    
    # Summary
    class_avg = df['average'].mean()
    console.print(f"\n[bold cyan]📊 Class Average: {class_avg:.1f}[/bold cyan]")
    
    highest_student = df.loc[df['average'].idxmax()]
    console.print(f"[bold green]🏆 Top Student: {highest_student['name']} ({highest_student['average']:.1f})[/bold green]")
    
    console.print("\n[italic]This calculator demonstrates UV's dependency management![/italic]")
    console.print("[dim]Dependencies: pandas, rich[/dim]")

if __name__ == "__main__":
    main()

Run the Calculator

uv run grade_calculator.py

This will:

  1. Load student data from the CSV file (or create sample data if needed)
  2. Calculate averages and assign letter grades for each student
  3. Display a beautiful grade report with color-coded grades
  4. Show class statistics including averages, highest/lowest scores, and standard deviation
  5. Generate a text report saved to grade_report.txt
  6. Display grade distribution showing how many students got each letter grade

Features demonstrated:

  • Data processing with pandas
  • Beautiful terminal output with rich
  • File I/O operations
  • Statistical calculations
  • Error handling for missing files
  • Progress indicators and status updates

Advanced Features

Virtual Environment Management

While UV handles virtual environments automatically, you can also manage them manually:

# Create a virtual environment
uv venv

# Create with specific Python version
uv venv --python 3.13

# Activate virtual environment (if needed)
source .venv/bin/activate  # On macOS/Linux
.venv\Scripts\activate     # On Windows

External Tools

Run tools from PyPI without permanent installation:

# Run httpie for API testing
uv run --with httpie http GET https://api.github.com/users/octocat

# Run cowsay for fun
uv run --with cowsay cowsay "UV is awesome!"

# Run black for code formatting
uv run --with black black --check .

Import and Export

Work with existing projects and requirements:

# Install from requirements.txt
uv pip install -r requirements.txt

# Export current dependencies
uv export --format requirements-txt --output-file requirements.txt
uv export --format pyproject-toml

Build and Publish

Package and distribute your projects:

# Build distribution packages
uv build

# Publish to PyPI
uv publish

Testing with UV

Setting up Tests

Add testing dependencies:

uv add --dev pytest pytest-cov

Create a simple test file test_example.py:

def add_numbers(a, b):
    return a + b

def multiply_numbers(a, b):
    return a * b

def test_add_numbers():
    assert add_numbers(2, 3) == 5
    assert add_numbers(-1, 1) == 0
    assert add_numbers(0, 0) == 0

def test_multiply_numbers():
    assert multiply_numbers(2, 3) == 6
    assert multiply_numbers(-1, 5) == -5
    assert multiply_numbers(0, 10) == 0

Run tests:

# Run all tests
uv run pytest

# Run with verbose output
uv run pytest -v

# Run with coverage
uv run pytest --cov

# Run specific test file
uv run pytest test_example.py

Performance Comparison

Here’s why UV is so much faster than traditional tools:

Operation pip UV Improvement
Install requests 12.3s 0.8s 15.4x faster
Install django 45.2s 2.1s 21.5x faster
Install tensorflow 180.5s 8.3s 21.7x faster
Create virtual environment 3.2s 0.1s 32x faster
Dependency resolution 8.7s 0.3s 29x faster
Why is UV so fast?
  1. Written in Rust: Compiled language with excellent performance
  2. Parallel Downloads: Downloads multiple packages simultaneously
  3. Smart Caching: Global cache shared across projects
  4. Advanced Resolution: Efficient SAT solver for dependencies
  5. Optimized Algorithms: Modern algorithms for package management

Best Practices

1. Project Organization

  • Always use uv init for new projects
  • Keep pyproject.toml clean and well-documented
  • Commit uv.lock to version control
  • Use meaningful project names and descriptions

2. Dependency Management

  • Pin critical dependency versions in pyproject.toml
  • Use uv sync regularly to keep environment updated
  • Separate development and production dependencies
  • Use optional dependencies for feature groups

3. Python Version Management

  • Specify Python version requirements in pyproject.toml
  • Test your project with multiple Python versions
  • Use uv run --python X.Y for version-specific testing
  • Document Python version requirements

4. Team Collaboration

  • Always commit pyproject.toml and uv.lock
  • Document special installation requirements
  • Use consistent Python versions across team
  • Provide clear setup instructions

5. Performance Tips

  • Use uv cache to share packages across projects
  • Pre-install commonly used Python versions
  • Use uv sync --frozen in production environments
  • Leverage UV’s parallel processing capabilities

Troubleshooting

Common Issues and Solutions

Issue: “uv: command not found”

# Solution: Restart terminal or reload shell
source ~/.bashrc  # or ~/.zshrc

Issue: Python version not found

# Solution: Install the Python version first
uv python install 3.13

Issue: Dependencies not resolving

# Solution: Clear cache and try again
uv cache clean
uv sync --refresh

Issue: Virtual environment issues

# Solution: Recreate virtual environment
rm -rf .venv
uv venv
uv sync

Useful Diagnostic Commands

# Check UV configuration
uv config

# Show project information
uv project info

# Check dependency tree
uv tree

# Validate project
uv check

# Show cache location and size
uv cache dir
uv cache size

# Clean cache
uv cache clean

Migration from Other Tools

From pip to UV

Replace your pip workflows:

# Old way
python -m venv myenv
source myenv/bin/activate
pip install requests beautifulsoup4

# New way
uv init myproject
cd myproject  
uv add requests beautifulsoup4

From Poetry to UV

Convert Poetry projects:

  1. Copy dependencies from pyproject.toml [tool.poetry.dependencies]
  2. Run uv init in project directory
  3. Add dependencies with uv add package_name
  4. Remove Poetry files if desired

From pipenv to UV

Replace Pipfile with UV:

  1. Extract packages from Pipfile
  2. Run uv init
  3. Add packages with uv add
  4. Use uv run instead of pipenv run

Example Workflow

Here’s a complete workflow for a new Python project:

# 1. Create and navigate to project directory
mkdir my_awesome_project && cd my_awesome_project

# 2. Initialize UV project
uv init

# 3. Add your dependencies
uv add requests beautifulsoup4 rich
uv add --dev pytest black mypy ruff

# 4. Install everything
uv sync

# 5. Run your application
uv run main.py

# 6. Run tests
uv run pytest

# 7. Format code
uv run black .

# 8. Type check
uv run mypy .

# 9. Lint code
uv run ruff check .

Conclusion

UV represents the future of Python package management. By combining speed, reliability, and modern features into a single tool, it simplifies Python development workflows dramatically.

Key Takeaways

  • Speed: UV is 10-100x faster than traditional tools
  • Simplicity: One tool replaces many separate utilities
  • Reliability: Advanced dependency resolution prevents conflicts
  • Modern: Built-in support for lock files and reproducible builds
  • Cross-platform: Consistent experience across operating systems

Next Steps

  1. Try UV Today: Install UV and experiment with your current projects
  2. Migrate Gradually: Start with new projects, then migrate existing ones
  3. Share with Team: Introduce UV to your development team
  4. Stay Updated: Follow UV development for new features
  5. Contribute: Report issues and contribute to the UV community

Resources

Happy coding with UV! 🚀

Did you know?

UV is developed by Astral, the same team behind Ruff (the ultra-fast Python linter). Their focus on performance and developer experience makes UV an excellent choice for modern Python development.

Citation

BibTeX citation:
@online{rasheduzzaman2025,
  author = {Md Rasheduzzaman and Rasheduzzaman, Md},
  title = {UV: {The} {Ultra-Fast} {Python} {Package} {Manager}},
  date = {2025-09-20},
  langid = {en}
}
For attribution, please cite this work as:
Md Rasheduzzaman, and Md Rasheduzzaman. 2025. “UV: The Ultra-Fast Python Package Manager.” September 20, 2025.

💬 Have thoughts or questions? Join the discussion below using your GitHub account!

You can edit or delete your own comments. Reactions like 👍 ❤️ 🚀 are also supported.