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

pantsbuild / pants / 18517631058

15 Oct 2025 04:18AM UTC coverage: 69.207% (-11.1%) from 80.267%
18517631058

Pull #22745

github

web-flow
Merge 642a76ca1 into 99919310e
Pull Request #22745: [windows] Add windows support in the stdio crate.

53815 of 77759 relevant lines covered (69.21%)

2.42 hits per line

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

55.17
/src/python/pants/backend/python/lint/isort/rules_integration_test.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
2✔
5

6
import re
2✔
7
from textwrap import dedent
2✔
8

9
import pytest
2✔
10

11
from pants.backend.python import target_types_rules
2✔
12
from pants.backend.python.lint.isort.rules import IsortFieldSet, IsortRequest
2✔
13
from pants.backend.python.lint.isort.rules import rules as isort_rules
2✔
14
from pants.backend.python.lint.isort.subsystem import Isort
2✔
15
from pants.backend.python.lint.isort.subsystem import rules as isort_subsystem_rules
2✔
16
from pants.backend.python.target_types import PythonSourcesGeneratorTarget
2✔
17
from pants.core.goals.fmt import FmtResult
2✔
18
from pants.core.util_rules import config_files, source_files
2✔
19
from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
2✔
20
from pants.engine.addresses import Address
2✔
21
from pants.engine.internals.scheduler import ExecutionError
2✔
22
from pants.engine.target import Target
2✔
23
from pants.testutil.python_interpreter_selection import all_major_minor_python_versions
2✔
24
from pants.testutil.rule_runner import QueryRule, RuleRunner
2✔
25

26

27
@pytest.fixture
2✔
28
def rule_runner() -> RuleRunner:
2✔
29
    return RuleRunner(
2✔
30
        rules=[
31
            *isort_rules(),
32
            *isort_subsystem_rules(),
33
            *source_files.rules(),
34
            *config_files.rules(),
35
            *target_types_rules.rules(),
36
            QueryRule(FmtResult, (IsortRequest.Batch,)),
37
            QueryRule(SourceFiles, (SourceFilesRequest,)),
38
        ],
39
        target_types=[PythonSourcesGeneratorTarget],
40
    )
41

42

43
GOOD_FILE = "from animals import cat, dog\n"
2✔
44
BAD_FILE = "from colors import green, blue\n"
2✔
45
FIXED_BAD_FILE = "from colors import blue, green\n"
2✔
46

47
# Note the `as` import is a new line.
48
NEEDS_CONFIG_FILE = "from colors import blue\nfrom colors import green as verde\n"
2✔
49
FIXED_NEEDS_CONFIG_FILE = "from colors import blue, green as verde\n"
2✔
50

51

52
def run_isort(
2✔
53
    rule_runner: RuleRunner,
54
    targets: list[Target],
55
    *,
56
    extra_args: list[str] | None = None,
57
) -> FmtResult:
58
    rule_runner.set_options(
2✔
59
        ["--backend-packages=pants.backend.python.lint.isort", *(extra_args or ())],
60
        env_inherit={"PATH", "PYENV_ROOT", "HOME"},
61
    )
62
    field_sets = [IsortFieldSet.create(tgt) for tgt in targets]
2✔
63
    input_sources = rule_runner.request(
2✔
64
        SourceFiles,
65
        [
66
            SourceFilesRequest(field_set.source for field_set in field_sets),
67
        ],
68
    )
69
    fmt_result = rule_runner.request(
2✔
70
        FmtResult,
71
        [
72
            IsortRequest.Batch(
73
                "",
74
                input_sources.snapshot.files,
75
                partition_metadata=None,
76
                snapshot=input_sources.snapshot,
77
            ),
78
        ],
79
    )
80
    return fmt_result
2✔
81

82

83
@pytest.mark.platform_specific_behavior
2✔
84
@pytest.mark.parametrize(
2✔
85
    "major_minor_interpreter",
86
    all_major_minor_python_versions(Isort.default_interpreter_constraints),
87
)
88
def test_passing_source(rule_runner: RuleRunner, major_minor_interpreter: str) -> None:
2✔
89
    rule_runner.write_files({"f.py": GOOD_FILE, "BUILD": "python_sources(name='t')"})
2✔
90
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.py"))
2✔
91
    fmt_result = run_isort(
2✔
92
        rule_runner,
93
        [tgt],
94
        extra_args=[f"--isort-interpreter-constraints=['=={major_minor_interpreter}.*']"],
95
    )
96
    assert fmt_result.stdout == ""
2✔
97
    assert fmt_result.output == rule_runner.make_snapshot({"f.py": GOOD_FILE})
2✔
98
    assert fmt_result.did_change is False
2✔
99

100

101
def test_failing_source(rule_runner: RuleRunner) -> None:
2✔
102
    rule_runner.write_files({"f.py": BAD_FILE, "BUILD": "python_sources(name='t')"})
×
103
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.py"))
×
104
    fmt_result = run_isort(rule_runner, [tgt])
×
105
    assert fmt_result.stdout == "Fixing f.py\n"
×
106
    assert fmt_result.output == rule_runner.make_snapshot({"f.py": FIXED_BAD_FILE})
×
107
    assert fmt_result.did_change is True
×
108

109

110
def test_multiple_targets(rule_runner: RuleRunner) -> None:
2✔
111
    rule_runner.write_files(
×
112
        {"good.py": GOOD_FILE, "bad.py": BAD_FILE, "BUILD": "python_sources(name='t')"}
113
    )
114
    tgts = [
×
115
        rule_runner.get_target(Address("", target_name="t", relative_file_path="good.py")),
116
        rule_runner.get_target(Address("", target_name="t", relative_file_path="bad.py")),
117
    ]
118
    fmt_result = run_isort(rule_runner, tgts)
×
119
    assert "Fixing bad.py\n" == fmt_result.stdout
×
120
    assert fmt_result.output == rule_runner.make_snapshot(
×
121
        {"good.py": GOOD_FILE, "bad.py": FIXED_BAD_FILE}
122
    )
123
    assert fmt_result.did_change is True
×
124

125

126
@pytest.mark.parametrize(
2✔
127
    "path,extra_args", ((".isort.cfg", []), ("custom.ini", ["--isort-config=custom.ini"]))
128
)
129
def test_config_file(rule_runner: RuleRunner, path: str, extra_args: list[str]) -> None:
2✔
130
    rule_runner.write_files(
×
131
        {
132
            "f.py": NEEDS_CONFIG_FILE,
133
            "BUILD": "python_sources(name='t', interpreter_constraints=['==3.9.*'])",
134
            path: "[isort]\ncombine_as_imports=True\n",
135
        }
136
    )
137
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.py"))
×
138
    fmt_result = run_isort(rule_runner, [tgt], extra_args=extra_args)
×
139
    assert fmt_result.stdout == "Fixing f.py\n"
×
140
    assert fmt_result.output == rule_runner.make_snapshot({"f.py": FIXED_NEEDS_CONFIG_FILE})
×
141
    assert fmt_result.did_change is True
×
142

143

144
def test_invalid_config_file(rule_runner: RuleRunner) -> None:
2✔
145
    """Reference https://github.com/pantsbuild/pants/issues/18618."""
146

147
    rule_runner.write_files(
×
148
        {
149
            ".isort.cfg": dedent(
150
                """\
151
        [settings]
152
        force_single_line = true
153
        # invalid setting:
154
        no_sections = this should be a bool, but isnt
155
        """
156
            ),
157
            "example.py": "from foo import bar, baz",
158
            "BUILD": "python_sources()",
159
        }
160
    )
161
    tgt = rule_runner.get_target(Address("", relative_file_path="example.py"))
×
162
    with pytest.raises(ExecutionError) as isort_error:
×
163
        run_isort(rule_runner, [tgt])
×
164
    assert any(
×
165
        re.search(r"Failed to pull configuration information from .*\.isort\.cfg", arg)
166
        for arg in isort_error.value.args
167
    )
168

169

170
def test_passthrough_args(rule_runner: RuleRunner) -> None:
2✔
171
    rule_runner.write_files({"f.py": NEEDS_CONFIG_FILE, "BUILD": "python_sources(name='t')"})
×
172
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.py"))
×
173
    fmt_result = run_isort(rule_runner, [tgt], extra_args=["--isort-args='--combine-as'"])
×
174
    assert fmt_result.stdout == "Fixing f.py\n"
×
175
    assert fmt_result.output == rule_runner.make_snapshot({"f.py": FIXED_NEEDS_CONFIG_FILE})
×
176
    assert fmt_result.did_change is True
×
177

178

179
def test_stub_files(rule_runner: RuleRunner) -> None:
2✔
180
    rule_runner.write_files(
×
181
        {
182
            "good.pyi": GOOD_FILE,
183
            "good.py": GOOD_FILE,
184
            "bad.pyi": BAD_FILE,
185
            "bad.py": BAD_FILE,
186
            "BUILD": "python_sources(name='t')",
187
        }
188
    )
189

190
    good_tgts = [
×
191
        rule_runner.get_target(Address("", target_name="t", relative_file_path="good.pyi")),
192
        rule_runner.get_target(Address("", target_name="t", relative_file_path="good.py")),
193
    ]
194
    fmt_result = run_isort(rule_runner, good_tgts)
×
195
    assert fmt_result.output == rule_runner.make_snapshot(
×
196
        {"good.py": GOOD_FILE, "good.pyi": GOOD_FILE}
197
    )
198
    assert not fmt_result.did_change
×
199

200
    bad_tgts = [
×
201
        rule_runner.get_target(Address("", target_name="t", relative_file_path="bad.pyi")),
202
        rule_runner.get_target(Address("", target_name="t", relative_file_path="bad.py")),
203
    ]
204
    fmt_result = run_isort(rule_runner, bad_tgts)
×
205
    assert fmt_result.stdout == "Fixing bad.py\nFixing bad.pyi\n"
×
206
    assert fmt_result.output == rule_runner.make_snapshot(
×
207
        {"bad.py": FIXED_BAD_FILE, "bad.pyi": FIXED_BAD_FILE}
208
    )
209
    assert fmt_result.did_change
×
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