Lately I’ve been hopping between projects that use different Rust, Python, and TypeScript package managers, and I kept having to turn back to documentation to keep their subcommands straight when switching from one back to another. So I made this cheat sheet for my own reference, and I might as well share it with the class too…
Project dependencies
Common package management tasks include:
- Prepare clone, or ensure the dependencies needed to work with the project are installed, such as after newly cloning the project’s sources.
- Audit checks the current dependency versions against a public database for known security vulnerabilities.
- Any outdated checks whether any dependencies have newer versions currently available, among either direct or indirect dependencies.
- Compatible outdated checks specifically for newer available versions that are compatible with the project’s dependency specification.
- Add adds a new regular dependency to the project, and add dev adds a new development-only dependency. This step includes locking the dependency.
- Update brings an application’s locked packages up-to-date with the latest compatible version.
In the package managers I’ve been using lately, these look like:
| cargo | uv | pip-tools [1] | pnpm | |
|---|---|---|---|---|
| Prepare clone | N/A | uv sync (optional) | pip-sync dev-requirements.txt | pnpm install |
| Audit | cargo audit [2] | uv run pip-audit | pip-audit | pnpm audit |
| Any outdated | cargo outdated [3] | uv tree --outdated | pip list --outdated [4] | pnpm outdated |
| Compatible outdated | cargo update -n | uv lock -Un | pip-compile -Un | pnpm outdated --compatible |
| What requires | cargo tree --invert | uv tree --invert --package | pipdeptree -r -p | pnpm why |
| Add | cargo add | uv add | Manually add to pyproject.toml, then run pip-compile | pnpm add |
| Add dev | cargo add --dev | uv add --dev | Add and run pip-compile | pnpm add --save-dev |
| Update | cargo update | uv lock -U | pip-compile -U | pnpm update |
| [1] | Also see the section below. |
| [2] | Requires the cargo-audit binary installed. See the section on running tools for instructions. |
| [3] | Requires the cargo-outdated binary. |
| [4] | Strictly speaking, this shows all outdated packages currently installed in your virtual environment, rather than outdated entries in requirements.txt. |
More about pip-tools
The commands in this column assume the installation of the pip-tools, pip-audit, pipdeptree packages in your virtual environment.
Dependencies are added by editing pyproject.toml, with development-only dependencies given as optional “dev” deps:
[project]
dependencies = [
"pelican[Markdown]",
"pelican-sitemap",
]
[project.optional-dependencies]
dev = [
"pip-tools",
"pipdeptree",
"pip-audit",
]
I then use pip-compile to generate requirements.txt:
pip-compile --generate-hashes pyproject.toml
And then dev-requirements.txt is compiled separately:
pip-compile --generate-hashes --allow-unsafe --extra=dev \
--constraint=requirements.txt --output-file=dev-requirements.txt \
pyproject.toml
Using the normal requirements.txt as a constraint ensures dev-requirements.txt will use the same versions of all the packages they share in common. --allow-unsafe allows pinning pip and setuptools versions for dev setups.
Version specifiers
These systems also have different ways of dealing with dependency versions, and it’s worth keeping those straight too.
Cargo dependencies use SemVer by default, with other types of specifiers possible. A dependency given as version ^0.2.3 will be considered compatible with an upgrade to 0.2.5 but not 0.3.1; one with ^1.2.3 can be upgraded to 1.2.4 or 1.3.5, but not 2.0.0. (The caret indicates SemVer, and is implicit if no other version specification type is given.)
Python version specifiers are described in the Python Packaging User Guide. These include a “compatible” specifier written as ~=1.2. This is different from Cargo’s caret in that only the unspecified version parts are allowed to vary: ~=1.2 considers the concrete version 1.2.4 to be compatible, but ~=1.2.3 does not. And unlike in Cargo, you can’t have hyphenated version suffices like -dev, though dotted ones like .pre are allowed.
By default, uv add applies a strict lower-bound specifier (>=1.2.3) instead of a “compatible” specifier.
Like with Cargo, npm dependency versions can be given with a caret for SemVer behavior. A tilde (~1.2.3) can be used to allow only patch-level upgrades.
Running tools
For installing and running tools from package repositories, not scoped to a particular project directory on your machine:
| cargo | uv | pnpm | |
|---|---|---|---|
| Run ephemerally | uvx | pnpm dlx | |
| Install | cargo install or cargo binstall [5] | uv tool install | pnpm install -g |
| List installed | cargo install-update --list --all [6] | uv tool list | pnpm list -g |
| Show outdated | cargo install-update --list --all | uv tool outdated | pnpm outdated -g |
| Update | cargo install-update | uv tool upgrade | pnpm update -g |
| Uninstall | cargo uninstall | uv tool uninstall | pnpm remove -g |
| [5] | binstall requires the cargo-binstall binary to be installed. |
| [6] | Requires the cargo-update binary installed. If the binary being updated was installed with binstall, this will update from the binary repo instead of downloading sources and compiling. |