Skip to content

Phase 2: replace black with ruff format #711

Description

@joshmarkovic

Summary

Follow-up to #707 (merged), which consolidated isort/flake8/pycln/absolufy-imports into Ruff. This completes the formatter half: replace black with ruff format, so a single tool handles both linting and formatting, configured entirely from the existing [tool.ruff] block in pyproject.toml.

Per @axellpadilla's request on #707, this should land only after the 1.10.1 final release (currently 1.10.1rc1) to avoid colliding with parallel work. Filing now to track it.

Why ruff format over black

This is not the "tool is unmaintained" case that motivated #707black is stable and still actively maintained (latest release ~June 2026; intentionally low-velocity by design). It's a consolidation-and-velocity case:

  • One toolchain, one binary, one pin. chore: consolidate isort, flake8, pycln and absolufy-imports into ruff #707 already moved linting to Ruff. Adopting ruff format means a single Rust binary and one [tool.ruff] config block cover both lint and format, instead of maintaining a separate black hook and version pin.
  • Speed. Warm --check over dbt/ + tests/ on this repo: ruff format0.06s vs black0.27s (~5×). Marginal in absolute terms at our size, but it compounds across pre-commit/CI and matches the lint half already on Ruff.
  • Zero dependency footprint. Ruff ships as a self-contained static binary with no Python dependencies. black 26.5.1 pulls in click, mypy-extensions, packaging, pathspec>=1.0.0, platformdirs, and pytokens. Notably black is itself a pathspec consumer — the same package that just required the override-dependencies workaround in 922b089. Dropping black removes one pathspec constraint and six transitive packages from the dev graph. (To be clear, this doesn't by itself resolve the mypy/pathspec conflict — that's a separate discussion — but it's one less moving part.)
  • Actively developed and widely adopted. Ruff is developed by Astral at high velocity (the v0.15 release + 2026 style guide, Feb 2026, is positioned as a full black/isort/flake8 replacement) and is used by FastAPI, Pydantic, and Hugging Face, among others.
  • Black-compatible by design — which is why this is low-risk. Ruff targets >99.9% black compatibility (measured on Django/Zulip). That's consistent with the tiny, purely-cosmetic diff below: switching tools barely changes the output.

Evidence (reproducible)

ruff 0.15.17 format vs black 26.5.1 on current master (d8fb26f), line-length 99, target py310:

11 files would be reformatted, 68 files already formatted

All 11 differences are cosmetic. Only one is in dbt/ source — sqlserver_relation.py, where ruff joins implicit f-string concatenations that now fit in 99 columns:

-            filter = (
-                f"{event_time_filter.field_name} >="
-                f" cast('{event_time_filter.start}' as datetime2)"
-            )
+            filter = (
+                f"{event_time_filter.field_name} >= cast('{event_time_filter.start}' as datetime2)"
+            )

The other 10 are all in tests/, mostly removing a blank line after a class header:

 class TestSimpleMaterializations(BaseSimpleMaterializations):
-
     def test_existing_view_materialization(self, project, models):

Proposal

  • In .pre-commit-config.yaml, replace the two black hooks (auto + black-check manual alias) with ruff-format (+ a manual ruff-format alias mirroring the current black-check pattern).
  • No new config: ruff format already reads line-length = 99 / target-version = "py310" from the [tool.ruff] block added in chore: consolidate isort, flake8, pycln and absolufy-imports into ruff #707.
  • Makefile: rename the black target to format; update the test target's formatter check.
  • One commit reformatting the 11 files above.

Trade-offs

  • One-time git blame churn on 11 files (10 of them tests). If that's a concern, the reformat commit can be listed in a .git-blame-ignore-revs file, which both git blame --ignore-revs-file and GitHub's blame view honor — optional given how small the diff is.
  • Diverges from dbt-labs/dbt-adapters, which uses black. Immaterial in practice given ruff's >99.9% black compatibility.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions