How uv Extra Flags Resolve Dependency Conflicts When Installing PyTorch

The uv package manager uses PEP 508 extras combined with a conflicts table in pyproject.toml to enforce mutually exclusive PyTorch variants, routing each extra flag to specific package indexes and preventing incompatible CUDA and CPU wheels from being installed simultaneously.

When managing PyTorch installations, dependency conflicts between CPU-only and CUDA-enabled wheels can break Python environments. The baonguyen6742/uv-install-torch repository demonstrates how uv extra flags resolve dependency conflicts by leveraging optional dependency groups and explicit conflict declarations to isolate incompatible binaries.

Declaring Mutually Exclusive Variants with Optional Dependencies

PyTorch distributes separate binary wheels for CPU-only and CUDA-enabled installations. To prevent these from colliding, the repository defines them as optional dependency groups that cannot coexist.

Defining CPU and CUDA Extra Groups

In pyproject.toml, the repository declares two optional dependency groups under [project.optional-dependencies]:

[project.optional-dependencies]
cpu   = ["torch==2.4.1", "torchvision==0.19.1", "torchaudio==2.4.1"]
cu124 = ["torch==2.4.1", "torchvision==0.19.1", "torchaudio==2.4.1"]

Both groups contain identical package names and versions, but they resolve against different binary wheels depending on which extra flag is passed during installation.

Enforcing Conflicts in pyproject.toml

To guarantee that both extras cannot be activated simultaneously, the repository includes a conflicts table in the [tool.uv] section:

[tool.uv]
conflicts = [[{ extra = "cpu" }, { extra = "cu124" }]]

Source: [pyproject.toml lines 30-33](https://github.com/baonguyen6742/uv-install-torch/blob/master/pyproject.toml#L30-L33)

This declaration instructs uv that the cpu and cu124 extras are mutually exclusive. When the resolver encounters a request to install both, it aborts with a clear error message rather than attempting to merge incompatible wheel variants.

Mapping uv Extra Flags to Package Indexes

Once the extras are defined, uv must know where to fetch the corresponding wheels. The repository uses the sources and index tables to route each extra to its specific PyTorch package index.

Configuring Source-Specific Indexes

The [tool.uv.sources] table maps each package to different indexes based on the active extra:

[tool.uv.sources]
torch = [
  { index = "pytorch-cpu",  extra = "cpu"   },
  { index = "pytorch-cu124", extra = "cu124" }
]
torchvision = [
  { index = "pytorch-cpu",  extra = "cpu"   },
  { index = "pytorch-cu124", extra = "cu124" }
]
torchaudio = [
  { index = "pytorch-cpu",  extra = "cpu"   },
  { index = "pytorch-cu124", extra = "cu124" }
]

Source: [pyproject.toml lines 37-50](https://github.com/baonguyen6742/uv-install-torch/blob/master/pyproject.toml#L37-L50)

When uv resolves dependencies with --extra cpu, it selects the pytorch-cpu index for all three packages. When --extra cu124 is passed, it switches to pytorch-cu124.

Explicit Index Definitions for PyTorch Wheels

The indexes themselves are defined in the [[tool.uv.index]] array with the explicit = true flag, ensuring they are only used when explicitly requested via the sources table:

[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"
explicit = true

[[tool.uv.index]]
name = "pytorch-cu124"
url = "https://download.pytorch.org/whl/cu124"
explicit = true

Source: [pyproject.toml lines 53-62](https://github.com/baonguyen6742/uv-install-torch/blob/master/pyproject.toml#L53-L62)

This configuration prevents uv from accidentally mixing wheels from different CUDA versions or CPU builds during resolution.

How the uv Resolver Prevents Dependency Conflicts

The uv resolver evaluates the full dependency graph while respecting the constraints defined in pyproject.toml, ensuring that only one PyTorch variant exists in the final environment.

Evaluating Extra-Specific Markers

During resolution, uv treats extras as conditional markers. The resolver activates only the dependency tree associated with the requested extra, ignoring the other. If both extras are requested simultaneously, the conflicts table triggers an immediate error:

uv sync --extra cu124 --extra cpu

error: Conflicting extras requested: 'cpu' and 'cu124' are mutually exclusive.

This prevents the installation of both torch-2.4.1+cpu and torch-2.4.1+cu124 in the same environment, which would otherwise cause import errors and runtime conflicts.

Lock File Generation with Conflict Isolation

The generated uv.lock file encodes the resolution result with markers that tie each package to its specific extra. For example, entries contain markers such as:


extra == 'extra-16-uv-install-torch-cpu'

or


extra == 'extra-16-uv-install-torch-cu124'

Source: uv.lock contains extra-specific markers throughout the file

These markers ensure that subsequent uv sync operations reproduce the exact same environment, and that the lock file remains valid only for the selected variant.

Installing PyTorch with uv Extra Flags

To install PyTorch using the conflict-resolution configuration, specify the desired hardware variant using the --extra flag.

Install the CUDA 12.4 Variant

To install PyTorch with CUDA 12.4 support, activate the cu124 extra:

uv sync --extra cu124

This command resolves torch, torchvision, and torchaudio from the pytorch-cu124 index and records the CUDA-enabled wheels in uv.lock.

Install the CPU-Only Variant

For CPU-only installations, use the cpu extra:

uv sync --extra cpu

The resolver fetches packages from the pytorch-cpu index, ensuring no CUDA libraries are installed.

Verify the Installation

After syncing, run the verification script to confirm the correct variant is active:

uv run main.py

When cu124 is selected, the script confirms CUDA device availability. When cpu is selected, it verifies CPU-only operation without CUDA.

Summary

  • Mutual exclusion via conflicts: The conflicts table in pyproject.toml explicitly declares that cpu and cu124 extras cannot coexist, preventing installation of incompatible PyTorch wheels.
  • Index routing with sources: The [tool.uv.sources] table maps each extra to a specific PyTorch index (pytorch-cpu or pytorch-cu124), ensuring the resolver fetches the correct binary variant.
  • Lock file isolation: The generated uv.lock encodes extra-specific markers, guaranteeing reproducible environments that contain only one PyTorch variant.
  • Command-line activation: Users activate the desired hardware target using uv sync --extra cpu or uv sync --extra cu124, with the resolver enforcing conflict rules at install time.

Frequently Asked Questions

What happens if I specify both --extra cpu and --extra cu124?

uv will abort with a clear error message stating that the extras are mutually exclusive. The conflicts table in pyproject.toml triggers this error before any packages are downloaded, protecting your environment from containing both CPU and CUDA variants of PyTorch simultaneously.

How does uv know which PyTorch wheel to download for each extra?

The [tool.uv.sources] table in pyproject.toml explicitly maps each package (torch, torchvision, torchaudio) to a specific index based on the active extra. When you pass --extra cu124, uv consults this table and routes the download request to the pytorch-cu124 index URL instead of the default PyPI.

Can I add more CUDA versions using the same conflict resolution pattern?

Yes. You can define additional extras such as cu118 or cu121 in [project.optional-dependencies], add corresponding entries to the conflicts table to ensure they are mutually exclusive with existing variants, and configure new explicit indexes in [[tool.uv.index]] mapped via [tool.uv.sources].

Where does uv store the information about which extra was selected?

The selection is recorded in the uv.lock file through extra-specific markers (e.g., extra == 'extra-16-uv-install-torch-cu124'). These markers ensure that subsequent uv sync operations reproduce the exact same dependency tree, and they prevent the lock file from being valid for conflicting extras simultaneously.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →