You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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 #707 — black 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 format ≈ 0.06s vs black ≈ 0.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).
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.
Summary
Follow-up to #707 (merged), which consolidated isort/flake8/pycln/absolufy-imports into Ruff. This completes the formatter half: replace
blackwithruff format, so a single tool handles both linting and formatting, configured entirely from the existing[tool.ruff]block inpyproject.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 formatoverblackThis is not the "tool is unmaintained" case that motivated #707 —
blackis stable and still actively maintained (latest release ~June 2026; intentionally low-velocity by design). It's a consolidation-and-velocity case:ruff formatmeans a single Rust binary and one[tool.ruff]config block cover both lint and format, instead of maintaining a separateblackhook and version pin.--checkoverdbt/+tests/on this repo:ruff format≈ 0.06s vsblack≈ 0.27s (~5×). Marginal in absolute terms at our size, but it compounds across pre-commit/CI and matches the lint half already on Ruff.black26.5.1 pulls inclick,mypy-extensions,packaging,pathspec>=1.0.0,platformdirs, andpytokens. Notablyblackis itself apathspecconsumer — the same package that just required theoverride-dependenciesworkaround in 922b089. Droppingblackremoves onepathspecconstraint 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.)Evidence (reproducible)
ruff 0.15.17 formatvsblack 26.5.1on current master (d8fb26f), line-length 99, target py310: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: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
.pre-commit-config.yaml, replace the twoblackhooks (auto +black-checkmanual alias) withruff-format(+ a manualruff-formatalias mirroring the currentblack-checkpattern).ruff formatalready readsline-length = 99/target-version = "py310"from the[tool.ruff]block added in chore: consolidate isort, flake8, pycln and absolufy-imports into ruff #707.blacktarget toformat; update thetesttarget's formatter check.Trade-offs
git blamechurn on 11 files (10 of them tests). If that's a concern, the reformat commit can be listed in a.git-blame-ignore-revsfile, which bothgit blame --ignore-revs-fileand GitHub's blame view honor — optional given how small the diff is.dbt-labs/dbt-adapters, which usesblack. Immaterial in practice given ruff's >99.9% black compatibility.