Skip to content

Replace black, isort and flake8 with ruff - #40

Merged
ali92hm merged 2 commits into
masterfrom
switch-to-ruff
Sep 28, 2026
Merged

ali92hm merged 2 commits into
masterfrom
switch-to-ruff

Conversation

@ali92hm

@ali92hm ali92hm commented Sep 28, 2026

Copy link
Copy Markdown
Owner

Consolidates linting, import sorting and formatting into a single tool, in both the repo and the template.

What changed

  • pyproject.toml (both trees): [tool.black]/[tool.isort] replaced with a single [tool.ruff] section. Rule selection (E, F, I, B, W) is scoped to what flake8+isort already checked, plus flake8-bugbear (B), which wasn't installed before.
  • Deleted both .flake8 files. Ruff reads pyproject.toml natively — the whole reason .flake8 existed as a separate file in the first place (flake8 still doesn't).
  • requirements.txt (both trees): black/isort/flake8 → ruff==0.16.9.
  • scripts/check-style.sh/fix-style.sh (both trees): ruff check/ruff format --check and ruff check --fix/ruff format.
  • .vscode/settings.json/extensions.json: swapped to charliermarsh.ruff, with format-on-save fix-all and import-sort actions.
  • Docs (README.md, tool-choices.md, project-structure.md, CLAUDE.md), .gitignore (.ruff_cache), clean.sh updated to match.

Two things found while verifying against the real tool (not assumed)

  1. Ruff's exclude/extend-exclude uses glob syntax, where {/} are brace-expansion metacharacters — unlike black's regex-based exclude, which treated the brace-named template directory as a literal match. Unescaped, ruff would crash trying to parse the template's Jinja-templated pyproject.toml as real TOML during config discovery, and would lint files inside it. Fixed by escaping the pattern ('\{\{cookiecutter.__project_name_kebab_case\}\}').
  2. flake8-bugbear (B011) caught assert False in the template's own scaffolded skipped-test placeholder, which flake8 alone never did (the plugin wasn't installed). Fixed both copies to raise AssertionError instead, per ruff's own suggested fix.

Testing

All run locally with real dependencies (Python 3.11), not simulated:

  • make check-style / make check-types — pass
  • make test-unit (with coverage) — 24/24 pass
  • make test-integration — 18/18 pass
  • make test-e2e (bakes the template and runs the generated project's own make ci/make build against the real [tool.ruff] config) — 2/2 pass
  • Confirmed ruff format --check . reports zero reformatting needed on the existing (previously black-formatted) codebase — the line-length = 88 / [tool.ruff.lint.pycodestyle] max-line-length = 120 split reproduces the old black-88/flake8-120 behavior exactly

🤖 Generated with Claude Code

ali92hm and others added 2 commits September 27, 2026 20:39
Consolidates linting, import sorting and formatting into a single tool
in both the repo and the template.

- pyproject.toml: [tool.black]/[tool.isort] replaced with a single
  [tool.ruff] section. Rule selection (E, F, I, B, W) is scoped to what
  flake8+isort already checked, plus flake8-bugbear (B), which wasn't
  installed before.
- Deleted both .flake8 files. Ruff reads pyproject.toml natively, which
  is the whole reason .flake8 existed as a separate file in the first
  place (flake8 still doesn't, see PyCQA/flake8#234).
- requirements.txt (both trees): black/isort/flake8 -> ruff==0.16.9.
- scripts/check-style.sh and fix-style.sh (both trees) now call
  `ruff check`/`ruff format --check` and `ruff check --fix`/`ruff format`.
- .vscode/settings.json and extensions.json: swapped to
  charliermarsh.ruff, with format-on-save fix-all and import-sort
  actions.
- Docs (README, tool-choices, project-structure, CLAUDE.md), .gitignore
  (.ruff_cache) and clean.sh updated to match.

Two things found while verifying against the real tool, not assumed:

- Ruff's exclude/extend-exclude uses glob syntax, where `{` and `}` are
  brace-expansion metacharacters, unlike black's regex-based exclude
  (which treated the brace-named template directory as a literal
  match). Unescaped, ruff would both crash trying to parse the
  template's Jinja-templated pyproject.toml as real TOML during config
  discovery, and lint files inside it. Fixed by escaping the pattern.
- flake8-bugbear (B011) caught `assert False` in the template's own
  scaffolded skipped-test placeholder, which flake8 alone never did
  (the plugin wasn't installed). Fixed both copies to
  `raise AssertionError` instead, per ruff's own suggested fix.

Verified locally with real dependencies (Python 3.11): check-style,
check-types, test-unit (with coverage), test-integration (18/18) and
test-e2e (2/2, which bakes the template and runs the generated
project's own make ci/build against the real ruff config) all pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The escaped-brace exclude pattern from the previous commit
('\{\{cookiecutter.__project_name_kebab_case\}\}') only worked by
accident on macOS/Linux. Per globset's own docs (the crate ruff uses
for exclude/extend-exclude matching), backslash escaping is enabled by
default on Unix platforms only, not on Windows. On Windows CI the
backslash was inert, `{`/`}` reverted to brace-alternation
metacharacters, the directory wasn't excluded, and ruff crashed trying
to parse the template's Jinja-templated pyproject.toml as real TOML:

    Cause: Failed to parse .../{{cookiecutter.__project_name_kebab_case}}/pyproject.toml
    Cause: TOML parse error ... invalid key-value pair, expected key

Switched to character-class escaping ([{] matches a literal `{`),
which globset documents as a separate, unconditional mechanism (not
gated behind the same platform-specific backslash_escape setting).

Verified locally on macOS: ruff check/format still correctly exclude
the template directory, and check-style/check-types/test-unit/
test-integration all pass. The actual cross-platform fix can only be
confirmed by the Windows leg of CI on this push.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@ali92hm
ali92hm merged commit c056418 into master Sep 28, 2026
12 checks passed
@ali92hm
ali92hm deleted the switch-to-ruff branch September 28, 2026 00:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant