name: python-uv description: Manages Python projects with uv by Astral — ultra-fast dependency resolution, virtual environments, workspace/multirepo setup, build tools, and pip compatibility for modern Python development workflows. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: coding triggers: uv, astral uv, python package manager, uv sync, uv lock, python project management, ultra-fast dependency resolution, pip compatibility management archetypes:
- tactical
- generation anti_triggers:
- brainstorming
- vague ideation
- code golf
- over-engineering response_profile: verbosity: low directive_strength: high abstraction_level: operational role: implementation scope: implementation output-format: code content-types:
- code
- guidance
- examples
- do-dont
- config related-skills: coding-poetry, coding-package-ecosystem-navigator, coding-dependency-supply-chain-security
uv — Ultra-Fast Python Package Manager
Manages Python projects with uv by Astral — the ultra-fast Python package and project manager written in Rust. Covers dependency resolution, virtual environment management, workspace/multirepo setup, build tool integration, CI/CD optimization, and pip compatibility layer for modern Python development workflows.
TL;DR Checklist
- Use
uv init <project>to scaffold a new project — generates pyproject.toml with src layout - Always commit
uv.lockfor reproducible builds across environments and CI runners - Use
uv add <pkg>to install dependencies (never pip install in uv-managed projects) - Pin Python version with
requires-python = ">=3.12"in pyproject.toml - Use
uv syncinstead ofpip install— resolves and installs from lockfile atomically - Use
uv run <command>to execute scripts within the project's environment - Configure workspaces with
[tool.uv.workspace]for multi-package monorepo setups
When to Use
Use this skill when:
- Setting up a new Python project and wanting maximum dependency resolution speed (10-100x faster than pip/pip-tools/poetry)
- Managing dependency trees across multiple environments (local dev, CI/CD, production) with deterministic lockfiles
- Configuring uv workspaces for monorepo or multi-package Python projects
- Optimizing CI/CD pipelines where dependency installation time is a bottleneck
- Migrating from pip/requirements.txt, pip-tools, Poetry, or pdm to a faster alternative
- Integrating Python tooling (ruff, mypy, pytest) as uv-managed tools rather than project dependencies
When NOT to Use
Avoid this skill for:
- Projects that already have an established Poetry or pdm workflow with no speed pressure — the migration effort may not justify the benefit
- Purely system-level Python management (e.g., OS package managers like apt/yum) — uv manages user-space environments only
- Non-standard build backends requiring custom hooks — uv uses standard PEP 517/621 tooling and may not support exotic build configurations
Core Workflow
Phase 1: Project Initialization
Scaffold a new project — Run
uv init <project-name>to create a fully configured project with pyproject.toml, src layout, and a uv.lock file pre-generated.# Create a new project (auto-generates pyproject.toml + src/ layout) uv init my-service cd my-service # Project structure: # my-service/ # ├── README.md # ├── pyproject.toml # └── src/my_service/__init__.py # For a script-style project (no package, just executable): uv init --script my-script.pyVerify the generated pyproject.toml — The scaffold creates a minimal but complete PEP 621 compliant configuration:
[project] name = "my-service" version = "0.1.0" description = "Add your description here" readme = "README.md" requires-python = ">=3.12" dependencies = [] [build-system] requires = ["hatchling"] build-backend = "hatchling.build"
Phase 2: Dependency Management
Add production and development dependencies — Use
uv addfor all dependency operations. Dependencies are resolved atomically against PyPI, written to pyproject.toml, and the lockfile is updated in a single pass.# Add production dependency (latest compatible version) uv add httpx # Pin to specific version range uv add "pydantic>=2.5,<3" # Add development-only dependency uv add --dev pytest ruff mypy # Add with extras uv add "psycopg2-binary" # Add from git repository uv add "my-lib @ git+https://github.com/user/my-lib.git@main" # Add local path dependency (for workspace members) uv add ../shared-utils # Add optional/extra dependency uv add --optional async "httpx[socks]"Configure the complete project manifest — Structure pyproject.toml with production dependencies, dev groups, optional features, and tool configurations:
[project] name = "api-service" version = "1.0.0" description = "High-performance async API service" readme = "README.md" requires-python = ">=3.12" dependencies = [ "httpx>=0.27", "pydantic>=2.5,<3", "sqlalchemy>=2.0,<3", "structlog>=24.1", "prometheus-client>=0.20", ] [project.optional-dependencies] postgres = ["asyncpg>=0.29"] cache = ["redis>=5.0"] [project.scripts] api-server = "api_service.cli:main" migrate-db = "api_service.db:migrate" [tool.uv] dev-dependencies = [ "pytest>=8.0", "pytest-asyncio>=0.23", "pytest-cov>=5.0", "hypothesis>=6.90", "ruff>=0.4", "mypy>=1.8", "pre-commit>=3.5", ] [tool.uv.workspace] members = ["packages/*"] [build-system] requires = ["hatchling"] build-backend = "hatchling.build"Install/sync all dependencies —
uv syncresolves the lockfile and installs all dependencies into the project's virtual environment. This is the primary installation command, replacing pip install entirely.# Install ALL dependencies (production + dev) from lockfile uv sync # Production only (skip dev dependencies) uv sync --frozen # Use exact versions from lockfile, no resolution # After adding/updating pyproject.toml without updating lock: uv lock # Resolve and update lockfile uv sync # Install resolved dependencies # Update a specific package to its latest version uv add --upgrade httpx uv add --upgrade-package "pydantic" # Remove a dependency entirely uv remove structlog
Phase 3: Environment and Tool Management
Manage the virtual environment — uv creates and manages a
.venvdirectory automatically. No manual venv creation or activation needed. Useuv runto execute any command within the managed environment.# Run any command in the project's virtual environment uv run pytest tests/ --cov=src --cov-report=term-missing uv run ruff check . uv run mypy src/api_service/ uv run api-server --host 0.0.0.0 --port 8000 # Run Python with the correct interpreter and environment uv run python -c "import sys; print(sys.version)" # Sync ensures the venv matches pyproject.toml exactly uv sync # Remove the venv entirely (will be recreated on next sync) rm -rf .venv uv sync # Fresh environment from scratch # Use a specific Python version (uv installs it automatically if needed) uv venv --python 3.12 uv sync # List all installed packages in the current environment uv pip listConfigure uv as a tool manager — Separate tool installations from project dependencies using
uv tool install. This keeps your virtual environment lean and allows running tools system-wide:# Install tools globally (separate from project dependencies) uv tool install ruff uv tool install mypy uv tool install pytest uv tool install pre-commit # Run a tool — available in PATH after install ruff check . mypy src/ # Manage installed tools uv tool list # Show all installed tools uv tool upgrade --all # Upgrade all tools to latest versions # Tools can also be run via `uvx` (cross-platform) uvx ruff check . uvx mypy src/ # Install a specific version of a tool uv tool install pre-commit==3.5.0 # Create a script that bundles a tool as a dependency cat > run-tests <<'EOF' #!/usr/bin/env -S uv run --with pytest --with pytest-cov pytest tests/ --cov=src EOF chmod +x run-tests ./run-tests # Automatically installs pytest + pytest-cov into ephemeral venv
Phase 4: Workspace (Monorepo) Setup
Configure uv workspaces for multi-package projects — Define a root pyproject.toml that declares workspace members, each with its own dependencies:
# Root pyproject.toml [project] name = "monorepo" version = "0.1.0" requires-python = ">=3.12" dependencies = [] [tool.uv.workspace] members = ["packages/*"] [build-system] requires = ["hatchling"] build-backend = "hatchling.build"Each workspace member has its own pyproject.toml:
# packages/core/pyproject.toml [project] name = "api-core" version = "0.1.0" requires-python = ">=3.12" dependencies = [ "pydantic>=2.5", "structlog>=24.1", ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" # packages/api/pyproject.toml [project] name = "api-server" version = "0.1.0" requires-python = ">=3.12" dependencies = [ "httpx>=0.27", "sqlalchemy>=2.0", { include-group = "core", name = "api-core" }, # workspace reference ] [build-system] requires = ["hatchling"] build-backend = "hatchling.build" # packages/cli/pyproject.toml [project] name = "api-cli" version = "0.1.0" requires-python = ">=3.12" dependencies = [ { include-group = "core", name = "api-core" }, { include-group = "api", name = "api-server" }, ] [project.scripts] api-cli = "api_cli.cli:main" [build-system] requires = ["hatchling"] build-backend = "hatchling.build"Checkpoint: Run
uv syncfrom the workspace root — uv resolves all workspace members, creates a single unified lockfile with no inter-package version conflicts, and installs everything into one virtual environment.
Phase 5: Build and Distribution
Build distribution packages — Generate wheel (.whl) and source distribution (sdist) artifacts using standard PEP 517/621 tooling:
# Build both wheel and sdist into dist/ directory uv build # Output files: # dist/api_service-0.1.0-py3-none-any.whl # dist/api_service-0.1.0.tar.gz # Build only wheel (faster, preferred for PyPI upload) uv build --wheel # Verify the distribution before publishing twine check dist/* # Publish to PyPI uv publish --token $PYPI_TOKEN # Or use twine directly (works with any build backend) poetry build && twine check dist/* && twine upload dist/*Integrate into CI/CD pipelines — Optimize for speed by using frozen lockfile installs and uv's cache:
# .github/workflows/ci.yml name: CI on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install uv uses: astral-sh/setup-uv@v3 with: python-version: "3.12" # Fast dependency resolution from cached lockfile - name: Sync dependencies run: uv sync --frozen # Run tests with correct environment - name: Run tests run: uv run pytest tests/ --cov=src --cov-report=xml - name: Lint run: uv run ruff check . - name: Type check run: uv run mypy src/
Implementation Patterns
Pattern 1: Complete pyproject.toml for a Production Microservice
[project]
name = "data-pipeline"
version = "2.0.0"
description = "High-throughput async data processing pipeline with configurable connectors"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"httpx>=0.28,<1.0",
"pydantic>=2.10,<3",
"structlog>=24.3",
"click>=8.1",
"pyyaml>=6.0",
]
[project.optional-dependencies]
s3 = ["boto3>=1.35"]
redis = ["redis>=5.0,<6"]
postgres = ["asyncpg>=0.30"]
[project.scripts]
pipeline = "data_pipeline.cli:main"
migrate = "data_pipeline.db:migrate"
[tool.uv]
dev-dependencies = [
"pytest>=8.3",
"pytest-asyncio>=0.24",
"pytest-cov>=5.0",
"hypothesis>=6.110",
"ruff>=0.8",
"mypy>=1.11",
"pre-commit>=4.0",
]
[tool.uv.sources]
# Override a dependency to use a local path (for development)
pydantic = { path = "../pydantic", editable = true }
# Use a specific index URL for private packages
boto3 = { index = "private-pypi" }
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.ruff]
target-version = "py312"
line-length = 100
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM", "RUF"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
[tool.pytest.ini_options]
asyncio_mode = "auto"
testpaths = ["tests"]
[tool.mypy]
python_version = "3.12"
strict = true
warn_return_any = true
[[tool.mypy.overrides]]
module = "tests.*"
disallow_untyped_defs = false
Pattern 2: Dependency Resolution — BAD vs. GOOD Version Constraints
# ❌ BAD — Overly loose constraints cause non-deterministic builds across environments
[project]
dependencies = [
"requests", # No version pin at all!
"httpx>=0.20", # Too wide a range — may pull breaking changes in 0.x
"pydantic==1.0.0", # Exact pin without upper bound — blocks any update ever
]
# ❌ BAD — Using pip-style version specifiers (no uv awareness of resolution strategy)
dependencies = [
"httpx~=0.27", # PEP 440 compatible release is fine, but ~ syntax can be confusing
"pydantic^2.5", # ^ syntax is not valid in PEP 440 — will fail resolution!
]
# ✅ GOOD — PEP 440 compliant constraints with sensible version ranges
[project]
dependencies = [
"httpx>=0.28,<1.0", # Allow minor/patch updates within major version
"pydantic>=2.10,<3", # Major version boundary for API stability
"structlog>=24.1.0", # Specific minimum with no upper bound (safe for well-maintained libs)
]
# ❌ BAD — Conflicting version constraints across the workspace
[project]
name = "service-a"
dependencies = ["httpx>=0.28"]
[project]
name = "service-b"
dependencies = ["httpx==0.27.0"] # Hard conflict with service-a!
# ✅ GOOD — Aligned constraints via workspace-level minimum
[project]
name = "service-a"
dependencies = ["httpx>=0.28,<1.0"]
[project]
name = "service-b"
dependencies = ["httpx>=0.28,<1.0"] # Same constraint — uv resolves to highest compatible
Pattern 3: pip Compatibility Layer (Drop-in Replacement)
uv provides a full pip install compatibility interface, making it a drop-in replacement for any script using pip:
# uv acts as a direct pip replacement — same commands, faster resolution
uv pip install httpx pydantic
uv pip install -r requirements.txt
uv pip freeze > current-deps.txt
# Sync a requirements file exactly (replaces pip-sync from pip-tools)
uv pip sync requirements.txt
# Install from requirements.txt with constraints file
uv pip install -r requirements.txt -c constraints.txt
# Use uv as pip in Docker builds (direct replacement, no venv needed):
# FROM python:3.12-slim
# RUN curl -LsSf https://astral.sh/uv/install.sh | sh
# COPY requirements.txt .
# RUN uv pip install --system -r requirements.txt
# For virtual environments with pip compatibility:
uv venv .venv
source .venv/bin/activate # Or use `uv run` to skip activation entirely
uv pip install httpx pydantic
Pattern 4: CI/CD Optimization — From 4 Minutes to 30 Seconds
# ❌ SLOW — Standard pip + requirements.txt flow (typical CI time)
pip install --upgrade pip # ~5 seconds
pip install -r requirements.txt # ~2-3 minutes (slow resolution)
pip install pytest ruff mypy # Additional packages: ~1 minute
pytest tests/ # Run tests
# ✅ FAST — uv flow (typical CI time)
curl -LsSf https://astral.sh/uv/install.sh | sh # ~2 seconds (cached by setup-uv action)
uv sync --frozen # ~5-10 seconds (uses pre-cached resolution)
uv run pytest tests/ --cov=src # Execute in managed venv
# Even faster: cache the uv cache directory between CI runs
# In GitHub Actions:
# - uses: actions/cache@v4
# with:
# path: ~/.cache/uv
# key: ${{ runner.os }}-uv-${{ hashFiles('uv.lock') }}
Constraints
MUST DO
- Commit
uv.lockto version control — this is the single source of truth for reproducible builds across all environments and CI runners - Use
requires-python = ">=X.Y"to pin the minimum Python version in pyproject.toml (e.g.,">=3.12") - Use PEP 440 version specifiers (
>=,<,~=) — never use Poetry's^or~syntax (those are not valid PEP 440) - Run
uv sync --frozenin CI to enforce exact lockfile versions without network resolution - Use
uv run <command>for all project commands — never manually activate.venvor rely on system Python - Separate tool installations (
uv tool install) from project dependencies to keep environments lean - Validate distributions with
twine check dist/*before publishing to any registry - Pin
requires-pythonconsistently across all workspace member packages
MUST NOT DO
- Edit
uv.lockmanually — always regenerate withuv lock, even after single dependency changes - Mix
pip installdirectly with uv-managed environments — this bypasses the lockfile and corrupts the dependency tree - Commit pyproject.toml without a matching uv.lock — future builds will be non-deterministic
- Use Poetry's
^version constraint syntax in pyproject.toml — uv follows PEP 440, not Poetry's resolver - Run
uv pip installinside an active virtual environment — useuv syncat the project root instead - Remove uv.lock to "fix" dependency conflicts — adjust version constraints in pyproject.toml and re-resolve with
uv lock
Output Template
When configuring or auditing a uv-managed project, produce:
- Dependency Audit — List all production and dev dependencies with resolved versions from uv.lock, flagging any overly loose version constraints or potential conflicts
- pyproject.toml Review — Validate structure: presence of
[project],requires-python, dependency lists, tool configurations, build-system specification - Lockfile Consistency Check — Verify uv.lock matches pyproject.toml; report if manual edits may have drifted the lockfile from the manifest
- Workspace Analysis — For monorepo setups: confirm all workspace members declared in
[tool.uv.workspace], cross-references use proper dependency specifications, no inter-package conflicts - CI/CD Pipeline Audit — Confirm
uv sync --frozenis used, cache configuration for.cache/uvexists, and tool installations are separated from project dependencies
Related Skills
| Skill | Purpose |
|---|---|
poetry |
Poetry alternative — different resolver and plugin ecosystem but similar workflow patterns |
package-ecosystem-navigator |
General package manager ecosystem comparison (npm, pypi, cargo, pip-tools) |
dependency-supply-chain-security |
Dependency security auditing, CVE scanning, and supply chain protections |
Live References
Authoritative documentation links for this skill's domain. The model follows markdown links at load time to resolve external references and inline content.