Skip to content

Add use_ruff option to replace isort, black, flake8, pyupgrade #333

@jensens

Description

@jensens

Summary

Add a [meta] use_ruff = true toggle in .meta.toml that switches from the current tool stack (isort + black + flake8 + pyupgrade) to ruff. Default keeps old tools; switch on = modern ruff setup.

Ruff replaces: isort, black, flake8, pyupgrade
Ruff does NOT replace: zpretty, codespell, check-manifest, pyroma, check-python-versions, i18ndude

The shared CI workflow (backend-lint.yml) already uses ruff, so this aligns generated packages with CI.


Implementation plan

Files to modify

1. src/plone/meta/config_package.py — Core logic

  • Add use_ruff cached_property reading [meta] use_ruff (default False)
  • Add _warn_ruff_incompatible_options() — warn if black_extra_lines, isort_extra_lines, flake8_extra_lines, or [flake8] extra_lines are set when ruff is active
  • pre_commit_config() — pass use_ruff to template context
  • pyproject_toml() — add ruff_extra_lines to options list, pass use_ruff
  • tox() — pass use_ruff to template context
  • flake8() — skip generation (return None) when use_ruff=True; git rm .flake8 if it exists from a prior run
  • warn_on_setup_cfg() — only warn about [flake8] section when not using ruff
  • configure() — call _warn_ruff_incompatible_options() early

2. src/plone/meta/default/pre-commit-config.yaml.j2 — Pre-commit hooks

Wrap in {% if use_ruff %} / {% else %}:

  • ruff path: single ruff-pre-commit repo with ruff-format + ruff (args: [--fix]) hooks
  • old path: pyupgrade, isort, black (unchanged)
  • zpretty stays unconditional
  • flake8 block wrapped in {% if not use_ruff %}
  • codespell, check-manifest, pyroma, check-python-versions, i18ndude stay unconditional

3. src/plone/meta/default/pyproject.toml.j2 — Tool config

Wrap in {% if use_ruff %} / {% else %}:

  • ruff path: [tool.ruff], [tool.ruff.lint] (select F/E/W/UP/I, ignore E501/E203), [tool.ruff.lint.isort] with Plone-style settings, plus %(ruff_extra_lines)s
  • old path: [tool.isort] + [tool.black] (unchanged)
  • [tool.check-manifest] ignore — make .flake8 entry conditional on {% if not use_ruff %}

4. src/plone/meta/default/tox-qa.j2 — Tox format env

Wrap format commands in {% if use_ruff %} / {% else %}:

  • ruff path: pre-commit run -a ruff-format, pre-commit run -a ruff
  • old path: pre-commit run -a pyupgrade, pre-commit run -a isort, pre-commit run -a black
  • pre-commit run -a zpretty stays unconditional

5. Documentation updates

  • docs/sources/reference/meta-toml.md — Document use_ruff in [meta] section + ruff_extra_lines in [pyproject] section
  • docs/sources/reference/generated-files.md — Note .flake8 is conditional; update .pre-commit-config.yaml and pyproject.toml descriptions
  • docs/sources/reference/tox-environments.md — Update format env description

Ruff config defaults

When use_ruff = true, pyproject.toml will include:

[tool.ruff]
target-version = "py310"

[tool.ruff.lint]
select = [
    # pyflakes
    "F",
    # pycodestyle
    "E",
    "W",
    # pyupgrade
    "UP",
    # isort
    "I",
]
ignore = [
    # line too long (formatter handles this)
    "E501",
    # whitespace before ':' (formatter handles slicing)
    "E203",
]

[tool.ruff.lint.isort]
force-single-line = true
from-first = true
lines-after-imports = 2
lines-between-types = 1
no-sections = true
order-by-type = false

Rationale for lint rules

  • F + E + W = flake8 core (pyflakes + pycodestyle) — matches current flake8 setup
  • UP = pyupgrade — replaces the standalone pyupgrade pre-commit hook
  • I = isort — replaces standalone isort
  • E501 ignored: ruff format handles line length (same as current flake8 config)
  • E203 ignored: ruff format handles whitespace in slicing (same as current flake8)
  • W503 does not exist in ruff, omitted
  • E231 handled by ruff format, omitted from ignore

Rationale for isort settings (matching Plone conventions)

  • force-single-line = true — one import per line (from isort plone profile)
  • from-first = truefrom imports before regular import statements
  • lines-after-imports = 2 — two blank lines after import block
  • lines-between-types = 1 — one blank line between import and from types
  • no-sections = true — no section separation, results in pure alphabetical sort (replaces isort's force_alphabetical_sort)
  • order-by-type = false — don't group by CONSTANT/Class/variable type

These settings are user-configurable via ruff_extra_lines in [pyproject].


Verification

  1. Run config-package on a test repo without use_ruff — confirm no changes to existing behavior
  2. Run config-package on a test repo with [meta] use_ruff = true — verify:
    • .pre-commit-config.yaml has ruff hooks, no pyupgrade/isort/black/flake8
    • pyproject.toml has [tool.ruff], no [tool.isort]/[tool.black]
    • .flake8 is not generated (removed if existed)
    • tox.ini format env uses ruff commands
  3. Run tox -e format and tox -e lint in the test repo to confirm ruff works

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions