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

pantsbuild / pants / 20332790708

18 Dec 2025 09:48AM UTC coverage: 64.992% (-15.3%) from 80.295%
20332790708

Pull #22949

github

web-flow
Merge f730a56cd into 407284c67
Pull Request #22949: Add experimental uv resolver for Python lockfiles

54 of 97 new or added lines in 5 files covered. (55.67%)

8270 existing lines in 295 files now uncovered.

48990 of 75379 relevant lines covered (64.99%)

1.81 hits per line

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

65.67
/src/python/pants/backend/python/lint/pydocstyle/rules_integration_test.py
1
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
1✔
5

6
from collections.abc import Sequence
1✔
7

8
import pytest
1✔
9

10
from pants.backend.python import target_types_rules
1✔
11
from pants.backend.python.lint.pydocstyle.rules import PydocstyleRequest
1✔
12
from pants.backend.python.lint.pydocstyle.rules import rules as pydocstyle_rules
1✔
13
from pants.backend.python.lint.pydocstyle.subsystem import PydocstyleFieldSet
1✔
14
from pants.backend.python.lint.pydocstyle.subsystem import rules as pydocstyle_subsystem_rules
1✔
15
from pants.backend.python.target_types import PythonSourcesGeneratorTarget
1✔
16
from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints
1✔
17
from pants.core.goals.lint import LintResult, Partitions
1✔
18
from pants.core.util_rules import config_files, source_files
1✔
19
from pants.engine.addresses import Address
1✔
20
from pants.engine.target import Target
1✔
21
from pants.testutil.python_interpreter_selection import all_major_minor_python_versions
1✔
22
from pants.testutil.rule_runner import QueryRule, RuleRunner
1✔
23

24

25
@pytest.fixture
1✔
26
def rule_runner() -> RuleRunner:
1✔
27
    return RuleRunner(
1✔
28
        rules=[
29
            *pydocstyle_rules(),
30
            *pydocstyle_subsystem_rules(),
31
            *source_files.rules(),
32
            *config_files.rules(),
33
            *target_types_rules.rules(),
34
            QueryRule(Partitions, [PydocstyleRequest.PartitionRequest]),
35
            QueryRule(LintResult, [PydocstyleRequest.Batch]),
36
        ],
37
        target_types=[PythonSourcesGeneratorTarget],
38
    )
39

40

41
GOOD_FILE = '''
1✔
42
"""Public module docstring is present."""
43
def fun():
44
  """Pretty docstring is present."""
45
  pass
46
'''
47
BAD_FILE = """
1✔
48
def fun():
49
  '''ugly docstring!'''
50
  pass
51
"""
52

53

54
def run_pydocstyle(
1✔
55
    rule_runner: RuleRunner, targets: list[Target], *, extra_args: list[str] | None = None
56
) -> Sequence[LintResult]:
57
    rule_runner.set_options(
1✔
58
        [
59
            "--backend-packages=pants.backend.python.lint.pydocstyle",
60
            *(extra_args or ()),
61
        ],
62
        env_inherit={"PATH", "PYENV_ROOT", "HOME"},
63
    )
64
    partitions = rule_runner.request(
1✔
65
        Partitions[PydocstyleFieldSet, InterpreterConstraints],
66
        [
67
            PydocstyleRequest.PartitionRequest(
68
                tuple(PydocstyleFieldSet.create(tgt) for tgt in targets)
69
            )
70
        ],
71
    )
72
    results = []
1✔
73
    for partition in partitions:
1✔
74
        result = rule_runner.request(
1✔
75
            LintResult,
76
            [PydocstyleRequest.Batch("", partition.elements, partition.metadata)],
77
        )
78
        results.append(result)
1✔
79
    return tuple(results)
1✔
80

81

82
def assert_success(
1✔
83
    rule_runner: RuleRunner, target: Target, *, extra_args: list[str] | None = None
84
) -> None:
85
    result = run_pydocstyle(rule_runner, [target], extra_args=extra_args)
1✔
86
    assert len(result) == 1
1✔
87
    assert result[0].exit_code == 0
1✔
88

89

90
@pytest.mark.platform_specific_behavior
1✔
91
@pytest.mark.parametrize(
1✔
92
    "major_minor_interpreter",
93
    all_major_minor_python_versions(["CPython>=3.9,<3.15"]),
94
)
95
def test_passing(rule_runner: RuleRunner, major_minor_interpreter: str) -> None:
1✔
96
    rule_runner.write_files({"f.py": GOOD_FILE, "BUILD": "python_sources(name='t')"})
1✔
97
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.py"))
1✔
98
    assert_success(
1✔
99
        rule_runner,
100
        tgt,
101
        extra_args=[f"--python-interpreter-constraints=['=={major_minor_interpreter}.*']"],
102
    )
103

104

105
def test_failing(rule_runner: RuleRunner) -> None:
1✔
UNCOV
106
    rule_runner.write_files({"f.py": BAD_FILE, "BUILD": "python_sources(name='t')"})
×
UNCOV
107
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.py"))
×
UNCOV
108
    result = run_pydocstyle(rule_runner, [tgt])
×
UNCOV
109
    assert len(result) == 1
×
UNCOV
110
    assert result[0].exit_code == 1
×
UNCOV
111
    assert "D100: Missing docstring in public module" in result[0].stdout
×
112

113

114
def test_multiple_targets(rule_runner: RuleRunner) -> None:
1✔
UNCOV
115
    rule_runner.write_files(
×
116
        {"good.py": GOOD_FILE, "bad.py": BAD_FILE, "BUILD": "python_sources(name='t')"}
117
    )
UNCOV
118
    tgts = [
×
119
        rule_runner.get_target(Address("", target_name="t", relative_file_path="good.py")),
120
        rule_runner.get_target(Address("", target_name="t", relative_file_path="bad.py")),
121
    ]
UNCOV
122
    result = run_pydocstyle(rule_runner, tgts)
×
UNCOV
123
    assert len(result) == 1
×
UNCOV
124
    assert result[0].exit_code == 1
×
UNCOV
125
    assert "good.py" not in result[0].stdout
×
UNCOV
126
    assert "D400: First line should end with a period (not '!')" in result[0].stdout
×
127

128

129
def test_respects_config_file(rule_runner: RuleRunner) -> None:
1✔
UNCOV
130
    rule_runner.write_files(
×
131
        {
132
            "f.py": BAD_FILE,
133
            "BUILD": "python_sources(name='t')",
134
            ".pydocstyle.ini": "[pydocstyle]\nignore = D100,D300,D400,D403",
135
        }
136
    )
UNCOV
137
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.py"))
×
UNCOV
138
    assert_success(rule_runner, tgt, extra_args=["--pydocstyle-config=.pydocstyle.ini"])
×
139

140

141
def test_respects_passthrough_args(rule_runner: RuleRunner) -> None:
1✔
UNCOV
142
    rule_runner.write_files({"f.py": BAD_FILE, "BUILD": "python_sources(name='t')"})
×
UNCOV
143
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.py"))
×
UNCOV
144
    assert_success(
×
145
        rule_runner, tgt, extra_args=["--pydocstyle-args='--ignore=D100,D300,D400,D403'"]
146
    )
147

148

149
def test_skip(rule_runner: RuleRunner) -> None:
1✔
UNCOV
150
    rule_runner.write_files({"f.py": BAD_FILE, "BUILD": "python_sources(name='t')"})
×
UNCOV
151
    tgt = rule_runner.get_target(Address("", target_name="t", relative_file_path="f.py"))
×
UNCOV
152
    result = run_pydocstyle(rule_runner, [tgt], extra_args=["--pydocstyle-skip"])
×
UNCOV
153
    assert not result
×
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