Resolving Conflicts Between Optional Dependencies (CPU vs cu124 Extras) with uv

Use the [tool.uv.conflicts] table in pyproject.toml to declare mutually exclusive extras like cpu and cu124, preventing uv from installing incompatible PyTorch wheels simultaneously.

The baonguyen6742/uv-install-torch repository demonstrates a robust pattern for resolving conflicts between optional dependencies when distributing PyTorch packages with uv. By leveraging uv's native conflict resolution and index management, developers can offer distinct CPU-only and CUDA-enabled installation paths while ensuring users cannot accidentally mix incompatible binaries.

Configuring Mutually Exclusive Extras in pyproject.toml

The solution centers on three specific sections within pyproject.toml that work together to present alternative installation paths while enforcing exclusivity.

Declaring Optional Dependencies

The [project.optional-dependencies] section defines two extras: cpu and cu124. Each extra lists identical packages—torch, torchvision, and torchaudio—but relies on subsequent configuration to resolve them against different wheel indexes.

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

Enforcing Conflicts with [tool.uv.conflicts]

The critical mechanism preventing simultaneous installation resides in the [tool.uv.conflicts] table. According to the source code in pyproject.toml, the following declaration instructs uv to treat these extras as mutually exclusive:

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

When a user attempts to install both extras simultaneously, uv reads this configuration and aborts immediately with a clear error message:

uv pip install .[cpu,cu124]

error: The extras "cpu" and "cu124" conflict with each other.

This prevents the runtime import errors and binary mismatches that would occur if CPU and CUDA wheels were mixed in the same environment.

Mapping Package Sources to Specific Indexes

The [tool.uv.sources] section maps each extra to its respective PyTorch repository index. The cpu extra pulls wheels from https://download.pytorch.org/whl/cpu, while cu124 targets https://download.pytorch.org/whl/cu124. This configuration ensures uv fetches the correct binaries without requiring manual URL handling or environment variables.

Installing PyTorch CPU vs CUDA-124 Variants

Depending on hardware availability, users select exactly one extra during installation.

Install the CPU-only variant:

uv pip install .[cpu]

Install the CUDA-124 variant:

uv pip install .[cu124]

Install only core dependencies (no PyTorch):

uv pip install .

This installs only the packages listed under [project.dependencies] (such as numpy or opencv-python), omitting the heavy PyTorch stack entirely.

Verifying the Installation with main.py

The repository includes main.py to validate that the correct wheel variant is active. After installation, running the script imports the packages and performs a sanity check:

python main.py

For CUDA installations, the output confirms GPU availability:


torch.cuda.is_available True
Device 0 :  name='NVIDIA GeForce RTX 3080', total_memory=...

For CPU installations, torch.cuda.is_available returns False, verifying that the CPU-only wheels from the pytorch-cpu index are in use.

Summary

  • [tool.uv.conflicts] in pyproject.toml explicitly declares that cpu and cu124 extras cannot coexist, preventing incompatible PyTorch installations.
  • [tool.uv.sources] directs uv to fetch wheels from distinct PyTorch indexes (pytorch-cpu vs pytorch-cu124) based on the selected extra.
  • Installation commands use bracket notation (.[cpu] or .[cu124]) to select the appropriate binary variant.
  • main.py provides runtime verification that the correct wheels (CPU or CUDA) are installed and functional.

Frequently Asked Questions

What happens if I try to install both cpu and cu124 extras simultaneously?

uv detects the conflict defined in [tool.uv.conflicts] and aborts the installation with the error: error: The extras "cpu" and "cu124" conflict with each other. This prevents the package manager from creating an environment with mixed CPU and CUDA binaries that would cause runtime failures.

How does uv know which PyTorch index to use for each extra?

The [tool.uv.sources] table in pyproject.toml maps the torch, torchvision, and torchaudio packages to specific indexes based on the active extra. When you specify .[cpu], uv queries the pytorch-cpu index (https://download.pytorch.org/whl/cpu); for .[cu124], it queries the pytorch-cu124 index.

Can I use this pattern for other mutually exclusive dependencies?

Yes. The conflicts table supports any optional extras defined in your project. You can declare multiple conflict groups or create complex exclusion rules (for example, tf-cpu vs tf-gpu) using the same [[{ extra = "name1" }, { extra = "name2" }]] syntax demonstrated in baonguyen6742/uv-install-torch.

Does this conflict resolution work with uv sync as well as uv pip install?

Yes. The [tool.uv.conflicts] configuration is respected across all uv commands that resolve dependencies, including uv sync and uv run. Whether installing from a pyproject.toml directly or locking dependencies into a uv.lock file, uv will enforce that conflicting extras cannot be selected together.

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 →