$title =

uv for Node and TypeScript Developers

;

$content = [

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

  • nodepython (runtime)
  • npm / pnpmuv (project + deps + env + lock + Python versions)
  • npm install <pkg>uv add <pkg> (adds to pyproject.toml, updates lock + env)
  • npm uninstall <pkg>uv remove <pkg>
  • package.jsonpyproject.toml
  • package-lock.json / pnpm-lock.yamluv.lock
  • node_modules/.venv/ (project env) plus uv’s global cache
  • npm run <script> / npm execuv run <cmd>
  • npx <tool>uvx <tool> or uv tool run <tool> (ephemeral tool env)
  • npm i -g <tool>uv tool install <tool> (isolated “global” tools)
  • nvmuv 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 by uv init and 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

(Astral Docs)

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

(Astral Docs)

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

(Astral Docs)


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) or
    • mypy (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 primitives
  • pandas = dataframes (like an in-memory DB table toolkit)
  • scikit-learn = classical ML
  • torch / 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

(Astral Docs)


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 ...
    Use uv add for project deps, or uv pip install when you intentionally want an ad-hoc install (for example inside a venv without persisting to pyproject.toml). (PyPI)

  • Thinking you must activate .venv
    You only need activation if you want to run commands without uv run. Using uv run is the preferred workflow. (Astral Docs)

  • CI that silently updates the lockfile
    Use --locked to fail fast when uv.lock does not match pyproject.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 .

];

$date =

;

$category =

,

;

$author =

;

Discover more from The Curious Programmer

Subscribe now to keep reading and get access to the full archive.

Continue reading