Contents

uv add or uv pip install - which?

Distortions in the uv

I’ve briefly mentioned having transitioned my Python workflow to use uv for Python version and dependency management. I want to elaborate a bit on dependency management with uv.

Let’s say I’ve started a python project with uv init and have a pyproject.toml which manages python version and project dependencies. To add dependencies, the options seem to be:

  1. uv add <dependency> or
  2. uv pip install <dependency> or
  3. add the dependency to pyproject.toml manually and then run uv sync.

Does it matter which you choose? Yes!

Within a project, adding a dependency to pyproject.toml manually and running uv sync works reliably, so a valid choice in that scenario, if not the most intuitive when first starting with uv.

What about uv pip install and uv add - are they completely interchangeable? This github issue comment kind of answers the question, but wraps up with a statement that seems to imply it’s a matter of preference.

/images/uv-add-vs-pip-install-gh-issue.png

There may be clearer guidance but I didn’t find it right away so I thought I’d verify when each works and when it doesn’t. The result ends up being really clear, which is useful!

TL;DR

  • uv add when there’s a pyproject.toml
  • un pip install when there’s not

Within a project

Let’s walk through this together.

Check pyproject.toml

I’ve used uv init to start a project, which means there’s a pyproject.toml to manage python version and project dependencies. Let’s see where we are with dependencies:

*** $  cat ../../pyproject.toml
[project]
name = "python-micrograd"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "graphviz>=0.20.3",
    "matplotlib>=3.10.0",
    "numpy>=2.2.2",
]

[tool.uv.workspace]
members = ["src/micrograd/my-project"]

Try uv pip install

Okay, graphviz, matplotlib, and numpy are the existing dependencies. Next, I decide I want to use PyTorch and PyTorch Vision + Audio in my project. Let’s try adding those with uv pip install torch first:

(python-micrograd) *** $  uv pip install torch
Resolved 9 packages in 517ms
Prepared 9 packages in 2.90s
Installed 9 packages in 155ms
 + filelock==3.17.0
 + fsspec==2025.2.0
 + jinja2==3.1.5
 + markupsafe==3.0.2
 + mpmath==1.3.0
 + networkx==3.4.2
 + sympy==1.13.1
 + torch==2.6.0
 + typing-extensions==4.12.2
(python-micrograd) *** $  uv pip install numpy
Audited 1 package in 2ms
(python-micrograd) *** $  uv pip install torchvision torchaudio
Resolved 13 packages in 249ms
Prepared 2 packages in 198ms
Installed 2 packages in 4ms
 + torchaudio==2.6.0
 + torchvision==0.21.0
(python-micrograd) *** $  code .
(python-micrograd) *** $  cat pyproject.toml
[project]
name = "python-micrograd"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "graphviz>=0.20.3",
    "matplotlib>=3.10.0",
    "numpy>=2.2.2",
]

[tool.uv.workspace]
members = ["src/micrograd/my-project"]

We see that the project’s .toml file hasn’t been updated with those dependencies. (nb: We already saw that matplotlib had been included previously but, even if we hadn’t checked earlier, the corresponding log here shows an Audited log message, not Resolved, Prepared, Installed.)

We wouldn’t expect uv sync to help since that synchronizes the environment with the content of pyproject.toml:

(python-micrograd) *** $  uv sync
Resolved 13 packages in 13ms
Uninstalled 11 packages in 741ms
 - filelock==3.17.0
 - fsspec==2025.2.0
 - jinja2==3.1.5
 - markupsafe==3.0.2
 - mpmath==1.3.0
 - networkx==3.4.2
 - sympy==1.13.1
 - torch==2.6.0
 - torchaudio==2.6.0
 - torchvision==0.21.0
 - typing-extensions==4.12.2
(python-micrograd) *** $  cat pyproject.toml
[project]
name = "python-micrograd"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "graphviz>=0.20.3",
    "matplotlib>=3.10.0",
    "numpy>=2.2.2",
]

[tool.uv.workspace]
members = ["src/micrograd/my-project"]

Try uv add

That went as expected. But we still want to see dependencies added to the project and we have one option remaining:

(python-micrograd) *** $  uv add torch
Resolved 37 packages in 557ms
Prepared 9 packages in 2.84s
Installed 9 packages in 165ms
 + filelock==3.17.0
 + fsspec==2025.2.0
 + jinja2==3.1.5
 + markupsafe==3.0.2
 + mpmath==1.3.0
 + networkx==3.4.2
 + sympy==1.13.1
 + torch==2.6.0
 + typing-extensions==4.12.2
(python-micrograd) *** $  cat pyproject.toml
[project]
name = "python-micrograd"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "graphviz>=0.20.3",
    "matplotlib>=3.10.0",
    "numpy>=2.2.2",
    "torch>=2.6.0",
]

[tool.uv.workspace]
members = ["src/micrograd/my-project"]

Verify that uv add is the way to go with pyproject.toml

Success! To recap where things stand: in the context of a Python project with a pyproject.toml, dependencies can’t be added using uv pip install. Let’s add another dependency:

(python-micrograd) *** $  uv add torchvision
Resolved 38 packages in 324ms
Prepared 1 package in 185ms
Installed 1 package in 4ms
 + torchvision==0.21.0
(python-micrograd) *** $  cat pyproject.toml
[project]
name = "python-micrograd"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "graphviz>=0.20.3",
    "matplotlib>=3.10.0",
    "numpy>=2.2.2",
    "torch>=2.6.0",
    "torchvision>=0.21.0",
]

[tool.uv.workspace]
members = ["src/micrograd/my-project"]

Cool. Because we used uv add, the dependency shows up in the project’s .toml file. We might also try adding another dependency with uv pip install; we know the dependency won’t be added to pyproject.toml, but can we at least run a script that imports that dependency?

import torch
import torchaudio

print(torch.__version__)
print(torchaudio.__version__)
(python-micrograd) *** $  uv run src/uv-tests/deps.py
Traceback (most recent call last):
  File "/Users/msyvr/code/create/portfolio/ml/micrograd/python-micrograd/src/uv-tests/deps.py", line 2, in <module>
    import torchaudio
ModuleNotFoundError: No module named 'torchaudio'

Nope. While we’re in the project environment and there’s a pyproject.toml, a dependency can’t be used if it’s not added to the .toml file first.

For good measure, let’s update the pyproject.toml using uv add torchaudio and verify that works:

(python-micrograd) *** $  uv add torchaudio
Resolved 39 packages in 221ms
Prepared 1 package in 193ms
Installed 1 package in 3ms
 + torchaudio==2.6.0
(python-micrograd) *** $  uv run src/uv-tests/deps.py
2.6.0
2.6.0
(python-micrograd) *** $  cat pyproject.toml
[project]
name = "python-micrograd"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
    "graphviz>=0.20.3",
    "matplotlib>=3.10.0",
    "numpy>=2.2.2",
    "torch>=2.6.0",
    "torchaudio>=2.6.0",
    "torchvision>=0.21.0",
]

[tool.uv.workspace]
members = ["src/micrograd/my-project"]

As you likely expected, torchaudio now shows up in pyproject.toml.

No project (i.e., no pyproject.toml)

Try uv pip install

Now, what if we leave the venv and use that same script in a non-project Python environment?

*** $  ls
deps.py          micrograd-rust   python-micrograd
*** $  uv run deps.py
Traceback (most recent call last):
  File "/Users/msyvr/code/create/portfolio/ml/micrograd/deps.py", line 1, in <module>
    import torch
ModuleNotFoundError: No module named 'torch'
*** $  uv venv
Using CPython 3.13.1
Creating virtual environment at: .venv
Activate with: source .venv/bin/activate
*** $  vact
(micrograd) *** $  uv run deps.py
Traceback (most recent call last):
  File "/Users/msyvr/code/create/portfolio/ml/micrograd/deps.py", line 1, in <module>
    import torch
ModuleNotFoundError: No module named 'torch'
(micrograd) *** $  uv pip install torch
Resolved 10 packages in 103ms
Prepared 3 packages in 2.26s
Installed 10 packages in 165ms
 + filelock==3.17.0
 + fsspec==2025.2.0
 + jinja2==3.1.5
 + markupsafe==3.0.2
 + mpmath==1.3.0
 + networkx==3.4.2
 + setuptools==75.8.0
 + sympy==1.13.1
 + torch==2.6.0
 + typing-extensions==4.12.2
(micrograd) *** $  uv run deps.py
/Users/msyvr/code/create/portfolio/ml/micrograd/.venv/lib/python3.13/site-packages/torch/_subclasses/functional_tensor.py:275: UserWarning: Failed to initialize NumPy: No module named 'numpy' (Triggered internally at /Users/runner/work/pytorch/pytorch/pytorch/torch/csrc/utils/tensor_numpy.cpp:81.)
  cpu = _conversion_method_template(device=torch.device("cpu"))
Traceback (most recent call last):
  File "/Users/msyvr/code/create/portfolio/ml/micrograd/deps.py", line 2, in <module>
    import torchaudio
ModuleNotFoundError: No module named 'torchaudio'
(micrograd) *** $  uv pip install torchaudio
Resolved 11 packages in 150ms
Prepared 1 package in 252ms
Installed 1 package in 3ms
 + torchaudio==2.6.0
(micrograd) *** $  uv run deps.py
/Users/msyvr/code/create/portfolio/ml/micrograd/.venv/lib/python3.13/site-packages/torch/_subclasses/functional_tensor.py:275: UserWarning: Failed to initialize NumPy: No module named 'numpy' (Triggered internally at /Users/runner/work/pytorch/pytorch/pytorch/torch/csrc/utils/tensor_numpy.cpp:81.)
  cpu = _conversion_method_template(device=torch.device("cpu"))
2.6.0
2.6.0

Since we’d expect something similar* to work in the absence of uv, it tracks that uv pip install is what one would use when there’s no pyproject.toml.

*The something similar would be pip install, as you’d expect.

Try uv add

And, finally, to confirm that uv add won’t work outside the scope of a project (i.e., no pyproject.toml):

(micrograd) *** $  uv pip uninstall torchaudio
Uninstalled 1 package in 36ms
 - torchaudio==2.6.0
(micrograd) *** $  uv run deps.py
/Users/msyvr/code/create/portfolio/ml/micrograd/.venv/lib/python3.13/site-packages/torch/_subclasses/functional_tensor.py:275: UserWarning: Failed to initialize NumPy: No module named 'numpy' (Triggered internally at /Users/runner/work/pytorch/pytorch/pytorch/torch/csrc/utils/tensor_numpy.cpp:81.)
  cpu = _conversion_method_template(device=torch.device("cpu"))
Traceback (most recent call last):
  File "/Users/msyvr/code/create/portfolio/ml/micrograd/deps.py", line 2, in <module>
    import torchaudio
ModuleNotFoundError: No module named 'torchaudio'
(micrograd) *** $  uv add torchaudio
error: No `pyproject.toml` found in current directory or any parent directory

Wrap up

This was a bit long, but we covered all the bases and verified when to use uv add s uv pip install (and also when uv sync might be warranted).

The following simple decision tree came out of that:

  1. Does your Python environment have a pyproject.toml?

No -> uv pip install

Yes -> uv add OR manually add dependency to pyproject.toml and then run uv sync