• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pantsbuild / pants / 19000741080

01 Nov 2025 06:16PM UTC coverage: 80.3% (+0.3%) from 80.004%
19000741080

Pull #22837

github

web-flow
Merge 51f49bc90 into da3fb359e
Pull Request #22837: Updated Treesitter dependencies

77994 of 97128 relevant lines covered (80.3%)

3.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.31
/src/python/pants/backend/python/lint/flake8/subsystem.py
1
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
4✔
5

6
from dataclasses import dataclass
4✔
7

8
from pants.backend.python.lint.first_party_plugins import (
4✔
9
    BaseFirstPartyPlugins,
10
    resolve_first_party_plugins,
11
)
12
from pants.backend.python.lint.flake8.skip_field import SkipFlake8Field
4✔
13
from pants.backend.python.subsystems.python_tool_base import PythonToolBase
4✔
14
from pants.backend.python.target_types import (
4✔
15
    ConsoleScript,
16
    InterpreterConstraintsField,
17
    PythonResolveField,
18
    PythonSourceField,
19
)
20
from pants.backend.python.util_rules import python_sources
4✔
21
from pants.core.goals.resolves import ExportableTool
4✔
22
from pants.core.util_rules.config_files import ConfigFilesRequest
4✔
23
from pants.engine.addresses import UnparsedAddressInputs
4✔
24
from pants.engine.rules import collect_rules, rule
4✔
25
from pants.engine.target import FieldSet, Target
4✔
26
from pants.engine.unions import UnionRule
4✔
27
from pants.option.option_types import (
4✔
28
    ArgsListOption,
29
    BoolOption,
30
    FileListOption,
31
    FileOption,
32
    SkipOption,
33
    TargetListOption,
34
)
35
from pants.util.docutil import doc_url
4✔
36
from pants.util.logging import LogLevel
4✔
37
from pants.util.strutil import softwrap
4✔
38

39

40
@dataclass(frozen=True)
4✔
41
class Flake8FieldSet(FieldSet):
4✔
42
    required_fields = (PythonSourceField,)
4✔
43

44
    source: PythonSourceField
4✔
45
    interpreter_constraints: InterpreterConstraintsField
4✔
46
    resolve: PythonResolveField
4✔
47

48
    @classmethod
4✔
49
    def opt_out(cls, tgt: Target) -> bool:
4✔
50
        return tgt.get(SkipFlake8Field).value
×
51

52

53
class Flake8(PythonToolBase):
4✔
54
    options_scope = "flake8"
4✔
55
    name = "Flake8"
4✔
56
    help_short = "The Flake8 Python linter (https://flake8.pycqa.org/)."
4✔
57

58
    default_main = ConsoleScript("flake8")
4✔
59
    default_requirements = ["flake8>=5.0.4,<7"]
4✔
60

61
    default_lockfile_resource = ("pants.backend.python.lint.flake8", "flake8.lock")
4✔
62

63
    skip = SkipOption("lint")
4✔
64
    args = ArgsListOption(example="--ignore E123,W456 --enable-extensions H111")
4✔
65
    config = FileOption(
4✔
66
        default=None,
67
        advanced=True,
68
        help=lambda cls: softwrap(
69
            f"""
70
            Path to an INI config file understood by Flake8
71
            (https://flake8.pycqa.org/en/latest/user/configuration.html).
72

73
            Setting this option will disable `[{cls.options_scope}].config_discovery`. Use
74
            this option if the config is located in a non-standard location.
75
            """
76
        ),
77
    )
78
    extra_files = FileListOption(
4✔
79
        default=None,
80
        advanced=True,
81
        help=softwrap(
82
            """Paths to extra files to include in the sandbox. This can be useful for Flake8 plugins,
83
            like including config files for the `flake8-bandit` plugin."""
84
        ),
85
    )
86
    config_discovery = BoolOption(
4✔
87
        default=True,
88
        advanced=True,
89
        help=lambda cls: softwrap(
90
            f"""
91
            If true, Pants will include any relevant config files during
92
            runs (`.flake8`, `flake8`, `setup.cfg`, and `tox.ini`).
93

94
            Use `[{cls.options_scope}].config` instead if your config is in a
95
            non-standard location.
96
            """
97
        ),
98
    )
99
    _source_plugins = TargetListOption(
4✔
100
        advanced=True,
101
        help=softwrap(
102
            f"""
103
            An optional list of `python_sources` target addresses to load first-party plugins.
104

105
            You must set the plugin's parent directory as a source root. For
106
            example, if your plugin is at `build-support/flake8/custom_plugin.py`, add
107
            `'build-support/flake8'` to `[source].root_patterns` in `pants.toml`. This is
108
            necessary for Pants to know how to tell Flake8 to discover your plugin. See
109
            {doc_url("docs/using-pants/key-concepts/source-roots")}
110

111
            You must also set `[flake8:local-plugins]` in your Flake8 config file.
112

113
            For example:
114

115
                [flake8:local-plugins]
116
                extension =
117
                CUSTOMCODE = custom_plugin:MyChecker
118

119
            While your plugin's code can depend on other first-party code and third-party
120
            requirements, all first-party dependencies of the plugin must live in the same
121
            directory or a subdirectory.
122

123
            To instead load third-party plugins, add them to a custom resolve alongside
124
            flake8 itself, as described in {doc_url("docs/python/overview/lockfiles#lockfiles-for-tools")}.
125
            """
126
        ),
127
    )
128

129
    @property
4✔
130
    def config_request(self) -> ConfigFilesRequest:
4✔
131
        # See https://flake8.pycqa.org/en/latest/user/configuration.html#configuration-locations
132
        # for how Flake8 discovers config files.
133
        return ConfigFilesRequest(
×
134
            specified=self.config,
135
            specified_option_name=f"[{self.options_scope}].config",
136
            discovery=self.config_discovery,
137
            check_existence=["flake8", ".flake8"],
138
            check_content={"setup.cfg": b"[flake8]", "tox.ini": b"[flake8]"},
139
        )
140

141
    @property
4✔
142
    def source_plugins(self) -> UnparsedAddressInputs:
4✔
143
        return UnparsedAddressInputs(
×
144
            self._source_plugins,
145
            owning_address=None,
146
            description_of_origin=f"the option `[{self.options_scope}].source_plugins`",
147
        )
148

149

150
# --------------------------------------------------------------------------------------
151
# First-party plugins
152
# --------------------------------------------------------------------------------------
153

154

155
class Flake8FirstPartyPlugins(BaseFirstPartyPlugins):
4✔
156
    pass
4✔
157

158

159
@rule(desc="Prepare [flake8].source_plugins", level=LogLevel.DEBUG)
4✔
160
async def flake8_first_party_plugins(flake8: Flake8) -> Flake8FirstPartyPlugins:
4✔
161
    return await resolve_first_party_plugins(flake8.source_plugins, Flake8FirstPartyPlugins)
×
162

163

164
def rules():
4✔
165
    return (
4✔
166
        *collect_rules(),
167
        *python_sources.rules(),
168
        UnionRule(ExportableTool, Flake8),
169
    )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc