Skip to content

Commit b9c52ca

Browse files
krishnan-chandrakaoscognifloyd
authored
Split ruff backend into separate backends for formatting and linting (#20545)
Closes #20525. This PR splits Ruff formatting out into a separate backend from Ruff linting/fixing, and registers the new backend as an experimental one. For backwards compatibility, we retain the existing lint/fix backend at `pants.backend.experimental.python.lint.ruff` and move the formatting backend out to `pants.backend.experimental.python.lint.ruff_fmt`. I tried to keep the changes as minimal as possible and not touch too many files / move too many things around. This will be a breaking change for anyone who has adopted the Ruff formatter in 2.20 pre-release, but that should be okay as the feature hasn't been released yet. The naming of the new backend is a bit confusing, but since all other formatter backends are published under the `lint` package I tried to keep the convention consistent. --------- Co-authored-by: Andreas Stenius <[email protected]> Co-authored-by: Jacob Floyd <[email protected]>
1 parent b3072b8 commit b9c52ca

File tree

17 files changed

+129
-61
lines changed

17 files changed

+129
-61
lines changed

docs/docs/python/overview/linters-and-formatters.mdx

+14-13
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,20 @@ Pants does several things to speed up running formatters and linters:
2626

2727
Linter/formatter support is implemented in separate [backends](../../using-pants/key-concepts/backends.mdx) so that they are easy to opt in to individually:
2828

29-
| Backend | Tool |
30-
| :-------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- |
31-
| `pants.backend.python.lint.bandit` | [Bandit](https://bandit.readthedocs.io/en/latest/): security linter |
32-
| `pants.backend.python.lint.black` | [Black](https://black.readthedocs.io/en/stable/): code formatter |
33-
| `pants.backend.python.lint.docformatter` | [Docformatter](https://pypi.org/project/docformatter/): docstring formatter |
34-
| `pants.backend.python.lint.flake8` | [Flake8](https://flake8.pycqa.org/en/latest/): style and bug linter |
35-
| `pants.backend.python.lint.isort` | [isort](https://readthedocs.org/projects/isort/): import statement formatter |
36-
| `pants.backend.python.lint.pydocstyle` | [Pydocstyle](https://pypi.org/project/pydocstyle/): docstring linter |
37-
| `pants.backend.python.lint.pylint` | [Pylint](https://pylint.pycqa.org/): style and bug linter |
38-
| `pants.backend.python.lint.yapf` | [Yapf](https://github.com/google/yapf): code formatter |
39-
| `pants.backend.python.lint.autoflake` | [Autoflake](https://github.com/myint/autoflake): remove unused imports |
40-
| `pants.backend.python.lint.pyupgrade` | [Pyupgrade](https://github.com/asottile/pyupgrade): automatically update code to use modern Python idioms like `f-strings` |
41-
| `pants.backend.experimental.python.lint.ruff` | [Ruff](https://beta.ruff.rs/docs/): an extremely fast Python linter, written in Rust. |
29+
| Backend | Tool |
30+
| :--------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------- |
31+
| `pants.backend.python.lint.bandit` | [Bandit](https://bandit.readthedocs.io/en/latest/): security linter |
32+
| `pants.backend.python.lint.black` | [Black](https://black.readthedocs.io/en/stable/): code formatter |
33+
| `pants.backend.python.lint.docformatter` | [Docformatter](https://pypi.org/project/docformatter/): docstring formatter |
34+
| `pants.backend.python.lint.flake8` | [Flake8](https://flake8.pycqa.org/en/latest/): style and bug linter |
35+
| `pants.backend.python.lint.isort` | [isort](https://readthedocs.org/projects/isort/): import statement formatter |
36+
| `pants.backend.python.lint.pydocstyle` | [Pydocstyle](https://pypi.org/project/pydocstyle/): docstring linter |
37+
| `pants.backend.python.lint.pylint` | [Pylint](https://pylint.pycqa.org/): style and bug linter |
38+
| `pants.backend.python.lint.yapf` | [Yapf](https://github.com/google/yapf): code formatter |
39+
| `pants.backend.python.lint.autoflake` | [Autoflake](https://github.com/myint/autoflake): remove unused imports |
40+
| `pants.backend.python.lint.pyupgrade` | [Pyupgrade](https://github.com/asottile/pyupgrade): automatically update code to use modern Python idioms like `f-strings` |
41+
| `pants.backend.experimental.python.lint.ruff.check` | [Ruff (for linting)](https://docs.astral.sh/ruff/linter/): an extremely fast Python linter, written in Rust. |
42+
| `pants.backend.experimental.python.lint.ruff.format` | [Ruff (for formatting)](https://docs.astral.sh/ruff/formatter/): an extremely fast Python code formatter, written in Rust. |
4243

4344
To enable, add the appropriate backends in `pants.toml`:
4445

docs/docs/using-pants/key-concepts/backends.mdx

+2-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ The list of all backends (both stable and experimental) is also available via `p
9898
| `pants.backend.experimental.python.framework.django` | Enables better support for projects using Django: [https://www.djangoproject.com](https://www.djangoproject.com) | |
9999
| `pants.backend.experimental.python.framework.stevedore` | Enables better support for projects using stevedore: [https://docs.openstack.org/stevedore/](https://docs.openstack.org/stevedore/) | |
100100
| `pants.backend.experimental.python.lint.add_trailing_comma` | Enables add-trailing-comma, a Python code formatter: [https://github.com/asottile/add-trailing-comma](https://github.com/asottile/add-trailing-comma) | [`add-trailing-comma`](../../../reference/subsystems/add-trailing-comma.mdx) |
101-
| `pants.backend.experimental.python.lint.ruff` | Enables Ruff, an extremely fast Python linter: [https://beta.ruff.rs/docs/](https://beta.ruff.rs/docs/) | [Linters and formatters](../../python/overview/linters-and-formatters.mdx) |
101+
| `pants.backend.experimental.python.lint.ruff.check` | Enables Ruff (for `lint`), an extremely fast Python linter: [https://docs.astral.sh/ruff/linter/](https://docs.astral.sh/ruff/linter/) | [Linters and formatters](../../python/overview/linters-and-formatters.mdx) |
102+
| `pants.backend.experimental.python.lint.ruff.format` | Enables Ruff (for `fmt`), an extremely fast Python code formatter: [https://docs.astral.sh/ruff/formatter/](https://docs.astral.sh/ruff/formatter/) | [Linters and formatters](../../python/overview/linters-and-formatters.mdx) |
102103
| `pants.backend.experimental.python.packaging.pyoxidizer` | Enables `pyoxidizer_binary` target. | [PyOxidizer](../../python/integrations/pyoxidizer.mdx) |
103104
| `pants.backend.experimental.python.typecheck.pyright` | Enables Pyright, a Python type checker: [https://github.com/microsoft/pyright](https://github.com/microsoft/pyright) | |
104105
| `pants.backend.experimental.python.typecheck.pytype` | Enables Pytype, a Python type checker: [https://google.github.io/pytype/](https://google.github.io/pytype/) | |

src/python/pants/backend/BUILD

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ __dependents_rules__(
3636
"[/build_files/fmt/yapf/register.py]",
3737
"[/python/lint/black/rules.py]",
3838
"[/python/lint/black/subsystem.py]",
39-
"[/python/lint/ruff/rules.py]",
39+
"[/python/lint/ruff/fmt_rules.py]",
40+
"[/python/lint/ruff/check_rules.py]",
4041
"[/python/lint/ruff/subsystem.py]",
4142
"[/python/lint/yapf/rules.py]",
4243
"[/python/lint/yapf/subsystem.py]",

src/python/pants/backend/build_files/fmt/ruff/integration_test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from pants.backend.build_files.fmt.ruff.register import RuffRequest
99
from pants.backend.build_files.fmt.ruff.register import rules as ruff_build_rules
10-
from pants.backend.python.lint.ruff.rules import rules as ruff_fmt_rules
10+
from pants.backend.python.lint.ruff.check_rules import rules as ruff_fmt_rules
1111
from pants.backend.python.lint.ruff.subsystem import Ruff
1212
from pants.backend.python.lint.ruff.subsystem import rules as ruff_subsystem_rules
1313
from pants.backend.python.target_types import PythonSourcesGeneratorTarget

src/python/pants/backend/build_files/fmt/ruff/register.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from pants.backend.build_files.fmt.base import FmtBuildFilesRequest
55
from pants.backend.python.lint.ruff import subsystem as ruff_subsystem
6-
from pants.backend.python.lint.ruff.rules import _run_ruff_fmt
6+
from pants.backend.python.lint.ruff.fmt_rules import _run_ruff_fmt
77
from pants.backend.python.lint.ruff.subsystem import Ruff
88
from pants.backend.python.subsystems.python_tool_base import get_lockfile_interpreter_constraints
99
from pants.core.goals.fmt import FmtResult
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
1+
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
22
# Licensed under the Apache License, Version 2.0 (see LICENSE).
33

44
python_sources()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
4+
python_sources()

src/python/pants/backend/experimental/python/lint/ruff/check/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
4+
"""Linter & formatter for Python.
5+
6+
See https://www.pantsbuild.org/docs/python-linters-and-formatters and https://docs.astral.sh/ruff/
7+
"""
8+
9+
from pants.backend.python.lint.ruff import check_rules as ruff_rules
10+
from pants.backend.python.lint.ruff import skip_field, subsystem
11+
12+
13+
def rules():
14+
return (*ruff_rules.rules(), *skip_field.rules(), *subsystem.rules())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
4+
python_sources()

src/python/pants/backend/experimental/python/lint/ruff/format/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
4+
"""Linter & formatter for Python.
5+
6+
See https://www.pantsbuild.org/docs/python-linters-and-formatters and https://docs.astral.sh/ruff/
7+
"""
8+
9+
from pants.backend.python.lint.ruff import fmt_rules as ruff_fmt_rules
10+
from pants.backend.python.lint.ruff import skip_field, subsystem
11+
12+
13+
def rules():
14+
return (*ruff_fmt_rules.rules(), *skip_field.rules(), *subsystem.rules())

src/python/pants/backend/experimental/python/lint/ruff/register.py

+9-3
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,15 @@
66
See https://www.pantsbuild.org/docs/python-linters-and-formatters and https://docs.astral.sh/ruff/
77
"""
88

9-
from pants.backend.python.lint.ruff import rules as ruff_rules
10-
from pants.backend.python.lint.ruff import skip_field, subsystem
9+
from pants.backend.experimental.python.lint.ruff.check import register as ruff_check
10+
from pants.base.deprecated import warn_or_error
1111

1212

1313
def rules():
14-
return (*ruff_rules.rules(), *skip_field.rules(), *subsystem.rules())
14+
warn_or_error(
15+
"2.23.0.dev0",
16+
"The `pants.backend.experimental.python.lint.ruff` backend",
17+
hint="Use `pants.backend.experimental.python.lint.ruff.check` instead.",
18+
start_version="2.20.0.dev7",
19+
)
20+
return ruff_check.rules()

src/python/pants/backend/python/lint/ruff/rules.py src/python/pants/backend/python/lint/ruff/check_rules.py

-37
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints
1414
from pants.backend.python.util_rules.pex import PexRequest, VenvPex, VenvPexProcess
1515
from pants.core.goals.fix import FixResult, FixTargetsRequest
16-
from pants.core.goals.fmt import AbstractFmtRequest, FmtResult, FmtTargetsRequest
1716
from pants.core.goals.lint import LintResult, LintTargetsRequest
1817
from pants.core.util_rules.config_files import ConfigFiles, ConfigFilesRequest
1918
from pants.core.util_rules.partitions import PartitionerType
@@ -59,43 +58,13 @@ def tool_id(cls) -> str:
5958
return RuffLintRequest.tool_id
6059

6160

62-
class RuffFormatRequest(FmtTargetsRequest):
63-
field_set_type = RuffFieldSet
64-
tool_subsystem = Ruff
65-
partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
66-
67-
@classproperty
68-
def tool_name(cls) -> str:
69-
return "ruff format"
70-
71-
@classproperty
72-
def tool_id(self) -> str:
73-
return "ruff-format"
74-
75-
7661
@dataclass(frozen=True)
7762
class _RunRuffRequest:
7863
snapshot: Snapshot
7964
mode: RuffMode
8065
interpreter_constraints: Optional[InterpreterConstraints] = None
8166

8267

83-
# Note - this function is kept separate because it is invoked from update_build_files.py, but
84-
# not as a rule.
85-
async def _run_ruff_fmt(
86-
request: AbstractFmtRequest.Batch,
87-
ruff: Ruff,
88-
interpreter_constraints: Optional[InterpreterConstraints] = None,
89-
) -> FmtResult:
90-
run_ruff_request = _RunRuffRequest(
91-
snapshot=request.snapshot,
92-
mode=RuffMode.FORMAT,
93-
interpreter_constraints=interpreter_constraints,
94-
)
95-
result = await _run_ruff(run_ruff_request, ruff)
96-
return await FmtResult.create(request, result)
97-
98-
9968
async def _run_ruff(
10069
request: _RunRuffRequest,
10170
ruff: Ruff,
@@ -169,16 +138,10 @@ async def ruff_lint(request: RuffLintRequest.Batch[RuffFieldSet, Any], ruff: Ruf
169138
return LintResult.create(request, result)
170139

171140

172-
@rule(desc="Format with `ruff format`", level=LogLevel.DEBUG)
173-
async def ruff_fmt(request: RuffFormatRequest.Batch, ruff: Ruff) -> FmtResult:
174-
return await _run_ruff_fmt(request, ruff)
175-
176-
177141
def rules():
178142
return [
179143
*collect_rules(),
180144
*RuffFixRequest.rules(),
181145
*RuffLintRequest.rules(),
182-
*RuffFormatRequest.rules(),
183146
*pex.rules(),
184147
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
4+
from typing import Optional
5+
6+
from pants.backend.python.lint.ruff.check_rules import _run_ruff, _RunRuffRequest
7+
from pants.backend.python.lint.ruff.subsystem import Ruff, RuffFieldSet, RuffMode
8+
from pants.backend.python.util_rules import pex
9+
from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints
10+
from pants.core.goals.fmt import AbstractFmtRequest, FmtResult, FmtTargetsRequest
11+
from pants.core.util_rules.partitions import PartitionerType
12+
from pants.engine.rules import collect_rules, rule
13+
from pants.util.logging import LogLevel
14+
from pants.util.meta import classproperty
15+
16+
17+
class RuffFormatRequest(FmtTargetsRequest):
18+
field_set_type = RuffFieldSet
19+
tool_subsystem = Ruff
20+
partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
21+
22+
@classproperty
23+
def tool_name(cls) -> str:
24+
return "ruff format"
25+
26+
@classproperty
27+
def tool_id(self) -> str:
28+
return "ruff-format"
29+
30+
31+
# Note - this function is kept separate because it is invoked from update_build_files.py, but
32+
# not as a rule.
33+
async def _run_ruff_fmt(
34+
request: AbstractFmtRequest.Batch,
35+
ruff: Ruff,
36+
interpreter_constraints: Optional[InterpreterConstraints] = None,
37+
) -> FmtResult:
38+
run_ruff_request = _RunRuffRequest(
39+
snapshot=request.snapshot,
40+
mode=RuffMode.FORMAT,
41+
interpreter_constraints=interpreter_constraints,
42+
)
43+
result = await _run_ruff(run_ruff_request, ruff)
44+
return await FmtResult.create(request, result)
45+
46+
47+
@rule(desc="Format with `ruff format`", level=LogLevel.DEBUG)
48+
async def ruff_fmt(request: RuffFormatRequest.Batch, ruff: Ruff) -> FmtResult:
49+
return await _run_ruff_fmt(request, ruff)
50+
51+
52+
def rules():
53+
return [
54+
*collect_rules(),
55+
*RuffFormatRequest.rules(),
56+
*pex.rules(),
57+
]

src/python/pants/backend/python/lint/ruff/rules_integration_test.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
from pants.backend.python import target_types_rules
1111
from pants.backend.python.lint.ruff import skip_field
12-
from pants.backend.python.lint.ruff.rules import RuffFixRequest, RuffFormatRequest, RuffLintRequest
13-
from pants.backend.python.lint.ruff.rules import rules as ruff_rules
12+
from pants.backend.python.lint.ruff.check_rules import RuffFixRequest, RuffLintRequest
13+
from pants.backend.python.lint.ruff.check_rules import rules as ruff_rules
14+
from pants.backend.python.lint.ruff.fmt_rules import RuffFormatRequest
15+
from pants.backend.python.lint.ruff.fmt_rules import rules as ruff_fmt_rules
1416
from pants.backend.python.lint.ruff.subsystem import RuffFieldSet
1517
from pants.backend.python.lint.ruff.subsystem import rules as ruff_subsystem_rules
1618
from pants.backend.python.target_types import PythonSourcesGeneratorTarget
@@ -35,6 +37,7 @@ def rule_runner() -> RuleRunner:
3537
return RuleRunner(
3638
rules=[
3739
*ruff_rules(),
40+
*ruff_fmt_rules(),
3841
*skip_field.rules(),
3942
*ruff_subsystem_rules(),
4043
*config_files.rules(),

src/python/pants/core/goals/update_build_files.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from pants.backend.python.goals import lockfile
2424
from pants.backend.python.lint.black.rules import _run_black
2525
from pants.backend.python.lint.black.subsystem import Black
26-
from pants.backend.python.lint.ruff.rules import _run_ruff_fmt
26+
from pants.backend.python.lint.ruff.fmt_rules import _run_ruff_fmt
2727
from pants.backend.python.lint.ruff.subsystem import Ruff
2828
from pants.backend.python.lint.yapf.rules import _run_yapf
2929
from pants.backend.python.lint.yapf.subsystem import Yapf

0 commit comments

Comments
 (0)