Install Python and the modern toolchain (uv, ruff, mypy)

Bootstrap a machine with uv, ruff, and mypy, and understand what each tool owns.

๐Ÿงฐ Module 0 7 min read Not started

Why this matters

In 2026, "I'm a Python developer" is not enough. You're expected to know uv for dependency management, ruff for linting/formatting, mypy / pyright for type checking, and pre-commit to enforce all of it. Teams that don't use this stack waste hours per week on environment bugs and bikeshedding style. This lesson installs the stack once so the rest of the course runs frictionlessly.

Learning objectives

  1. Install a modern Python (3.12+) on Windows / macOS / Linux.
  2. Use uv for everything (install, sync, run).
  3. Configure ruff, mypy, pre-commit in a fresh project.
  4. Pick an editor (VS Code / Cursor / PyCharm) and configure it.
  5. Know the difference between user, system, and project Pythons.

1. Install Python via uv

The old advice ("install Python from python.org") is fine but increasingly replaced by tools like uv (Astral) which manage Pythons for you โ€” like Node's nvm or Rust's rustup.

Install uv

OSCommand
Windows (winget)winget install astral-sh.uv
Windows (PowerShell)irm https://astral.sh/uv/install.ps1 | iex
macOS / Linuxcurl -LsSf https://astral.sh/uv/install.sh | sh

Verify:

powershell
uv --version

Install Python with uv

powershell
uv python install 3.12 3.13
uv python list

uv keeps the binaries in ~/.local/share/uv/python/ (Linux/macOS) or %LOCALAPPDATA%\uv\python\ (Windows). No PATH wrangling.

To "use" a Python version in a project, you don't change PATH; you run uv run python ... and uv selects the right one based on the project's .python-version file.


2. Three kinds of Python on your machine

This trips up everyone:

TypeWhereWhen used
System Python/usr/bin/python3, ships with OSOS tools depend on it; never touch
User Pythoninstalled by you with uv / pyenv / installerDefault REPL, scripts
Project Pythonvirtual environment inside the projectWhat uv sync activates

The cardinal rule: one venv per project. Don't install third-party packages into your user or system Python.

uv makes this automatic: uv add httpx creates the venv if needed and installs into it.


3. Bootstrap your "lab" project

powershell
uv init py-lab --python 3.12
cd py-lab

This creates:

py-lab/
โ”œโ”€โ”€ .python-version       # "3.12"
โ”œโ”€โ”€ pyproject.toml        # project metadata + deps
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ hello.py
โ””โ”€โ”€ .gitignore

Add some packages:

powershell
uv add ruff mypy pytest pytest-cov hypothesis ipython
uv add --dev pre-commit

uv add updates pyproject.toml, locks resolved versions in uv.lock, and installs into .venv/.

Run anything in the env:

powershell
uv run python hello.py
uv run pytest
uv run ruff check .

You almost never need to activate the venv explicitly with uv โ€” uv run handles it.


4. Configure ruff

ruff (Astral) replaces flake8, isort, black, pylint, pyupgrade โ€” one tool, written in Rust, ~100ร— faster.

Add to pyproject.toml:

toml
[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
# Pick a sensible default set; you can expand later
select = [
    "E",    # pycodestyle errors
    "W",    # pycodestyle warnings
    "F",    # pyflakes
    "I",    # isort (import order)
    "B",    # flake8-bugbear
    "UP",   # pyupgrade (modernise syntax)
    "C4",   # comprehensions
    "SIM",  # simplify
    "PTH",  # use pathlib
    "RUF",  # ruff-specific
]
ignore = [
    "E501",      # line too long (use formatter)
]

[tool.ruff.format]
quote-style = "double"
indent-style = "space"

Use:

powershell
uv run ruff check .          # lint
uv run ruff check . --fix    # auto-fix
uv run ruff format .         # format (replaces black)

5. Configure mypy

Static type checker. Catches a class of bugs before runtime.

toml
[tool.mypy]
python_version = "3.12"
strict = true
warn_unused_ignores = true
warn_return_any = true
plugins = ["pydantic.mypy"]

# Per-module overrides (e.g., for libraries without type stubs)
[[tool.mypy.overrides]]
module = ["some_untyped_lib.*"]
ignore_missing_imports = true

strict = true is the right default. It will complain at first โ€” that's working as intended. We cover typing in depth in Phase 3.5.

Run:

powershell
uv run mypy src/

For a faster alternative, try pyright (Microsoft): uv add --dev pyright. Both are excellent; pyright is what most VS Code/Cursor extensions use.


6. Configure pre-commit

Run lint/format/type-check automatically on every git commit.

.pre-commit-config.yaml:

yaml
repos:
  - repo: https://github.com/astral-sh/ruff-pre-commit
    rev: v0.7.0           # pin a version; bump periodically
    hooks:
      - id: ruff
        args: [--fix]
      - id: ruff-format

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.13.0
    hooks:
      - id: mypy
        additional_dependencies: [pydantic]
        args: [--strict]

  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files
        args: [--maxkb=500]

Install:

powershell
uv run pre-commit install

Now every git commit runs the hooks. Bad commits get auto-fixed or blocked. The team's style stays consistent without anyone arguing about it.


7. Editor setup

Install these extensions:

  • Python (Microsoft)
  • Pylance (Microsoft, includes pyright)
  • Ruff (Astral)
  • Jupyter (Microsoft) โ€” for notebooks

Workspace settings (.vscode/settings.json):

json
{
  "python.defaultInterpreterPath": ".venv/bin/python",
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.organizeImports": "explicit",
    "source.fixAll": "explicit"
  },
  "[python]": {
    "editor.defaultFormatter": "charliermarsh.ruff"
  },
  "python.testing.pytestEnabled": true,
  "python.analysis.typeCheckingMode": "strict"
}

On Windows, use .venv\Scripts\python.exe for defaultInterpreterPath.

PyCharm

File โ†’ Settings โ†’ Project โ†’ Python Interpreter โ†’ point to .venv. Install the Ruff plugin.

Terminal / Vim / Neovim

Use pyright, ruff-lsp, and your usual setup. The CLI tools work identically.


8. Reference pyproject.toml

A complete starter โ€” copy into any new project:

toml
[project]
name = "py-lab"
version = "0.1.0"
description = "Sandbox for the Python course"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "httpx>=0.27",
    "pydantic>=2.7",
]

[project.optional-dependencies]
dev = [
    "ruff>=0.7",
    "mypy>=1.13",
    "pytest>=8",
    "pytest-cov>=5",
    "hypothesis>=6",
    "pre-commit>=3",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "UP", "C4", "SIM", "PTH", "RUF"]
ignore = ["E501"]

[tool.ruff.format]
quote-style = "double"

[tool.mypy]
python_version = "3.12"
strict = true

[tool.pytest.ini_options]
addopts = "-ra -q --cov=src"
testpaths = ["tests"]

9. The "is my environment healthy?" command

Save as scripts/check.ps1 (or check.sh):

powershell
uv sync
uv run ruff check .
uv run ruff format --check .
uv run mypy src/
uv run pytest

Run before every push. Same script runs in CI (Phase 6 + 9).


10. Common mistakes to avoid

  1. pip install ... outside a venv โ†’ pollutes user Python. Always uv add.
  2. Multiple Python versions on PATH โ†’ run python --version and where python (Windows) / which python (macOS/Linux). Should be one.
  3. Forgetting uv sync after pulling teammate's commits โ†’ "but it works on my machine."
  4. Committing .venv/ to git. Add to .gitignore (uv init does this).
  5. Hand-editing uv.lock. Never. Let uv manage it.

Hands-on lab (1 hour)

  1. Install uv and Python 3.12 via uv python install 3.12.
  2. uv init my-first-tool; cd in.
  3. Add httpx, ruff, mypy, pytest.
  4. Create src/my_first_tool/main.py that fetches https://httpbin.org/get and prints the JSON.
  5. Add ruff and mypy config to pyproject.toml from this lesson.
  6. Run all four checks (ruff lint, ruff format, mypy, pytest) and fix any errors.
  7. Initialise git; install pre-commit; make a commit.
  8. Bonus: push to GitHub and add a GitHub Actions workflow that runs the same checks.

Self-check

  1. Why use uv instead of pip + venv?
  2. What is the difference between system, user, and project Python?
  3. What does ruff format replace?
  4. Why use pre-commit?
  5. What does pyproject.toml describe?

References

Sign in to save your progress and earn badges.