Summary
Removing a dependency with uv rm also strips the entry from the [tool.uv.sources] table, because uv treats that entry as part of the installed package definition.
To keep the source declaration while un‑installing the package, you must manipulate the pyproject.toml manually (or with a script) rather than using uv rm.
Root Cause
uv rm <pkg>removes all references to<pkg>in the project file, including the custom source line under[tool.uv.sources].- The
editable=truesource mapping is considered part of the package specification, souvcannot distinguish “remove the locked version” from “keep the source entry”.
Why This Happens in Real Systems
- Unified command model:
uvaims to keep the manifest consistent; deleting a package should leave no dangling configuration. - Editable installs are treated as a single source of truth—if the package isn’t listed as a dependency, its source entry is deemed irrelevant.
- This behavior mirrors other packaging tools (e.g.,
pip uninstallremoves the requirement line from arequirements.txt).
Real-World Impact
- Developer friction: Every time a library’s own dependencies change, developers must:
- Delete the source entry.
- Re‑add the library to re‑inject the source line.
- CI/CD instability: Automated pipelines that
uv rma package can unintentionally corrupt thepyproject.toml, causing builds to fail until the source line is restored. - Jupyter notebook churn: Restarting kernels does not reload the updated source, forcing the manual cycle described above.
Example or Code (if necessary and relevant)
# pyproject.toml (excerpt)
[tool.uv.sources]
mylib = { path = "/PATH/TO/MY/LIBRARY", editable = true }
[project]
dependencies = [
"mylib",
# other deps…
]
How Senior Engineers Fix It
- Edit
pyproject.tomldirectly: Removemylibfrom thedependencieslist but leave the entry under[tool.uv.sources]. - Automate with a tiny script to strip the dependency while preserving the source line:
import tomllib, tomli_w, pathlib
proj = pathlib.Path(“pyproject.toml”)
data = tomllib.loads(proj.read_text())
Remove from dependencies
deps = data.get(“project”, {}).get(“dependencies”, [])
data[“project”][“dependencies”] = [d for d in deps if not d.startswith(“mylib”)]
proj.write_text(tomli_w.dumps(data))
- **Use `uv sync`** after editing: `uv sync` will reinstall the remaining dependencies, respecting the unchanged source entry.
- **Pin the library version** in the main project (e.g., `mylib @ file:///PATH/TO/MY/LIBRARY`) instead of relying on `[tool.uv.sources]`; this keeps the source reference even if the package is removed from `dependencies`.
## Why Juniors Miss It
- **Assume** `uv rm` behaves like a simple uninstall, not realizing it also edits the source table.
- **Overlook** the separation between *dependency list* and *source mapping* in `pyproject.toml`.
- **Rely** on interactive tools (e.g., Jupyter) and miss the necessity of a static file edit, leading to repeated trial‑and‑error cycles.