If you come from Node/TypeScript, uv will feel familiar: it is basically npm + npx + pipx + pyenv + lockfile discipline, but for Python. uv is a single tool that covers project dependencies, virtual environments, Python version management, and publishing.
This guide maps the concepts you already know (npm, npx, lockfiles, toolchains) onto uv’s model, so you can be productive fast without turning your ML repo into a cursed snowflake.
The Mental Model
Node/npm concept → Python/uv analog
node→python(runtime)npm/pnpm→uv(project + deps + env + lock + Python versions)npm install <pkg>→uv add <pkg>(adds topyproject.toml, updates lock + env)npm uninstall <pkg>→uv remove <pkg>package.json→pyproject.tomlpackage-lock.json/pnpm-lock.yaml→uv.locknode_modules/→.venv/(project env) plus uv’s global cachenpm run <script>/npm exec→uv run <cmd>npx <tool>→uvx <tool>oruv tool run <tool>(ephemeral tool env)npm i -g <tool>→uv tool install <tool>(isolated “global” tools)nvm→uv python install+uv python pin
uv is not centered on an “active” venv. Each project gets a dedicated .venv/, and the preferred execution path is uv run ...
90-Second Quick Start (The Only Part Most People Need)
uv init myproj
cd myproj
uv add fastapi
uv add --dev ruff pytest
uv run python -c "print('ok')"
uv run ruff check .
uv run pytest
uv run keeps the project locked and synced before running your command, so you usually do not need to manually “install” anything. (Astral Docs)
What uv Creates (Project Anatomy)
A uv-managed project is basically these files and directories:
pyproject.toml
Your project metadata + dependencies + tool configuration (ruff, mypy/pyright, etc.)uv.lock
Exact, reproducible resolution. Commit this..venv/
Project virtual environment created automatically on first project command..python-version
Project Python version pin (created byuv initand used by uv’s Python management).
uv will create .venv and uv.lock the first time you run a project command like uv run, uv sync, or uv lock. (Astral Docs)
Core Commands You Will Actually Use
Create a project (like npm init)
uv init myproj
cd myproj
Add dependencies (like npm install <pkg>)
uv add requests
uv add "httpx>=0.20"
uv add "httpx @ git+https://github.com/encode/httpx"
Dev dependencies (the “dev” group):
uv add --dev pytest ruff
uv add updates pyproject.toml, and also updates the lockfile and environment automatically. (Astral Docs)
Remove dependencies (like npm uninstall)
uv remove requests
uv remove --dev pytest
Run commands (like npm run or npm exec)
uv run python -V
uv run pytest
uv run ruff check .
Before every uv run, uv verifies the lockfile matches pyproject.toml and that .venv matches the lockfile. (Astral Docs)
Lock and sync (manual mode)
Most of the time, uv does this for you automatically. When you want to do it explicitly:
uv lock
uv sync
Manual uv sync is useful when you want your editor/IDE to pick up dependency changes immediately. (Astral Docs)
Reproducibility: “npm ci” Energy (CI and Teams)
uv has two important “do not mutate stuff” modes:
- Locked: fail if the lockfile would change (closest to
npm ci) - Frozen: do not update the lockfile, even if it is stale
Examples:
uv sync --locked
uv run --locked pytest
uv sync --frozen
uv run --frozen pytest
--locked raises an error if the lockfile is out of date. --frozen uses the lockfile without checking whether it is up to date. (Astral Docs)
Equivalent environment variables exist (handy in CI):
UV_LOCKED=1(equivalent to--locked)UV_FROZEN=1(equivalent to--frozen) (Astral Docs)
Dev Dependencies and “Scripts”
Where dev deps live
uv reads development dependencies from the standardized [dependency-groups] table (PEP 735). The dev group is synced by default. (Astral Docs)
A typical pyproject.toml ends up looking like:
[project]
name = "myproj"
version = "0.1.0"
dependencies = [
"fastapi>=0.115",
]
[dependency-groups]
dev = [
"pytest",
"ruff",
]
Task running
Unlike npm scripts, most Python projects just run commands directly:
uv run pytest
uv run ruff check .
If you want named tasks, use just, make, or task. A clean approach is installing the runner as a tool:
uv tool install just
Python Versions (Like Node + nvm)
uv can install and manage Python versions, so you are not stuck wrestling with system Python.
uv python install 3.12
uv python pin 3.12
uv python find
uv run python -V
The uv python subcommands are first-class in uv. (Astral Docs)
Global and Ephemeral CLI Tools (pipx and npx, but less annoying)
Install a tool “globally” (isolated, like pipx):
uv tool install ruff@latest
ruff --version
Run a tool ephemerally (like npx):
uvx ruff check .
uvx --from httpie http --help
uvx is an alias for uv tool run. (GitHub)
Recommended Tooling Stack (TypeScript Developer Defaults)
If you want the “ESLint + Prettier + tests” vibe in Python:
- Lint + format: Ruff (
ruff check,ruff format) (Astral Docs) -
Type checking:
pyright(fast, great DX) ormypy(deep typing ecosystem)
- Tests:
pytest
Install:
uv add --dev ruff pyright pytest
Run:
uv run ruff check .
uv run ruff format .
uv run pyright
uv run pytest
ML and Data Science Basics (Mapped to Your Mental Models)
numpy= fast arrays and math primitivespandas= dataframes (like an in-memory DB table toolkit)scikit-learn= classical MLtorch/tensorflow= deep learning
Install a common stack:
uv add numpy pandas scikit-learn
uv add --dev ipykernel
Jupyter that actually uses your project environment
Start Jupyter with access to the project environment:
uv run --with jupyter jupyter lab
If you want notebook installs to land in the right place, create a dedicated kernel:
uv add --dev ipykernel
uv run ipython kernel install --user --env VIRTUAL_ENV $(pwd)/.venv --name=myproj
uv run --with jupyter jupyter lab
Monorepos: uv Workspaces (Cargo-style)
uv supports workspaces for multi-package repos. A workspace is declared with tool.uv.workspace in the workspace root pyproject.toml. (Astral Docs)
In a workspace, uv sync --all-packages updates the shared environment with all workspace members installed. (Astral Docs)
Publishing (Like npm publish)
uv supports building distributions and publishing to an index:
uv build
uv publish
That is the happy path for “build wheel/sdist” and upload. (Astral Docs)
Common Pitfalls and Fixes
-
Installing into system Python with
pip install ...
Useuv addfor project deps, oruv pip installwhen you intentionally want an ad-hoc install (for example inside a venv without persisting topyproject.toml). (PyPI) -
Thinking you must activate
.venv
You only need activation if you want to run commands withoutuv run. Usinguv runis the preferred workflow. (Astral Docs) -
CI that silently updates the lockfile
Use--lockedto fail fast whenuv.lockdoes not matchpyproject.toml. (Astral Docs) -
Build failures on macOS or Linux when wheels are missing
This usually means you are building from source because there is no compatible wheel. Install the relevant system build tooling, or choose versions with wheels available. (Astral Docs)
Quick Start Cheat Sheet
# Create project
uv init myproj && cd myproj
# Add deps
uv add fastapi
uv add --dev ruff pytest
# Run in locked, reproducible env
uv run python -c "print('ok')"
uv run ruff check .
uv run pytest
# CI-style strictness
uv sync --locked
uv run --locked pytest
# Global tools and ephemeral runs
uv tool install ruff@latest
uvx ruff check .