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

pantsbuild / pants / 19015773527

02 Nov 2025 05:33PM UTC coverage: 17.872% (-62.4%) from 80.3%
19015773527

Pull #22816

github

web-flow
Merge a12d75757 into 6c024e162
Pull Request #22816: Update Pants internal Python to 3.14

4 of 5 new or added lines in 3 files covered. (80.0%)

28452 existing lines in 683 files now uncovered.

9831 of 55007 relevant lines covered (17.87%)

0.18 hits per line

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

0.0
/src/python/pants/backend/python/typecheck/pytype/rules.py
1
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

UNCOV
4
from __future__ import annotations
×
5

UNCOV
6
import logging
×
UNCOV
7
from collections.abc import Iterable
×
UNCOV
8
from dataclasses import dataclass
×
9

UNCOV
10
from pants.backend.python.subsystems.setup import PythonSetup
×
UNCOV
11
from pants.backend.python.target_types import (
×
12
    InterpreterConstraintsField,
13
    PythonResolveField,
14
    PythonSourceField,
15
)
UNCOV
16
from pants.backend.python.typecheck.pytype.skip_field import SkipPytypeField
×
UNCOV
17
from pants.backend.python.typecheck.pytype.subsystem import Pytype
×
UNCOV
18
from pants.backend.python.util_rules import pex_from_targets
×
UNCOV
19
from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints
×
UNCOV
20
from pants.backend.python.util_rules.partition import (
×
21
    _partition_by_interpreter_constraints_and_resolve,
22
)
UNCOV
23
from pants.backend.python.util_rules.pex import (
×
24
    PexRequest,
25
    VenvPexProcess,
26
    VenvPexRequest,
27
    create_pex,
28
    create_venv_pex,
29
)
UNCOV
30
from pants.backend.python.util_rules.pex_environment import PexEnvironment
×
UNCOV
31
from pants.backend.python.util_rules.pex_from_targets import RequirementsPexRequest
×
UNCOV
32
from pants.core.goals.check import CheckRequest, CheckResult, CheckResults
×
UNCOV
33
from pants.core.goals.resolves import ExportableTool
×
UNCOV
34
from pants.core.util_rules import config_files
×
UNCOV
35
from pants.core.util_rules.config_files import find_config_file
×
UNCOV
36
from pants.core.util_rules.source_files import SourceFilesRequest, determine_source_files
×
UNCOV
37
from pants.engine.collection import Collection
×
UNCOV
38
from pants.engine.internals.graph import resolve_coarsened_targets as coarsened_targets_get
×
UNCOV
39
from pants.engine.internals.native_engine import MergeDigests
×
UNCOV
40
from pants.engine.internals.selectors import concurrently
×
UNCOV
41
from pants.engine.intrinsics import execute_process, merge_digests
×
UNCOV
42
from pants.engine.rules import Rule, collect_rules, implicitly, rule
×
UNCOV
43
from pants.engine.target import CoarsenedTargets, CoarsenedTargetsRequest, FieldSet, Target
×
UNCOV
44
from pants.engine.unions import UnionRule
×
UNCOV
45
from pants.util.logging import LogLevel
×
UNCOV
46
from pants.util.ordered_set import FrozenOrderedSet, OrderedSet
×
UNCOV
47
from pants.util.strutil import pluralize
×
48

UNCOV
49
logger = logging.getLogger(__name__)
×
50

51

UNCOV
52
@dataclass(frozen=True)
×
UNCOV
53
class PytypeFieldSet(FieldSet):
×
UNCOV
54
    required_fields = (PythonSourceField,)
×
55

UNCOV
56
    sources: PythonSourceField
×
UNCOV
57
    resolve: PythonResolveField
×
UNCOV
58
    interpreter_constraints: InterpreterConstraintsField
×
59

UNCOV
60
    @classmethod
×
UNCOV
61
    def opt_out(cls, tgt: Target) -> bool:
×
62
        return tgt.get(SkipPytypeField).value
×
63

64

UNCOV
65
class PytypeRequest(CheckRequest):
×
UNCOV
66
    field_set_type = PytypeFieldSet
×
UNCOV
67
    tool_name = Pytype.options_scope
×
68

69

UNCOV
70
@dataclass(frozen=True)
×
UNCOV
71
class PytypePartition:
×
UNCOV
72
    field_sets: FrozenOrderedSet[PytypeFieldSet]
×
UNCOV
73
    root_targets: CoarsenedTargets
×
UNCOV
74
    resolve_description: str | None
×
UNCOV
75
    interpreter_constraints: InterpreterConstraints
×
76

UNCOV
77
    def description(self) -> str:
×
UNCOV
78
        ics = str(sorted(str(c) for c in self.interpreter_constraints))
×
UNCOV
79
        return f"{self.resolve_description}, {ics}" if self.resolve_description else ics
×
80

81

UNCOV
82
class PytypePartitions(Collection[PytypePartition]):
×
UNCOV
83
    pass
×
84

85

UNCOV
86
@rule(
×
87
    desc="Pytype typecheck each partition based on its interpreter_constraints",
88
    level=LogLevel.DEBUG,
89
)
UNCOV
90
async def pytype_typecheck_partition(
×
91
    partition: PytypePartition,
92
    pytype: Pytype,
93
    pex_environment: PexEnvironment,
94
) -> CheckResult:
95
    roots_sources, requirements_pex, pytype_pex, config_files = await concurrently(
×
96
        determine_source_files(SourceFilesRequest(fs.sources for fs in partition.field_sets)),
97
        create_pex(
98
            **implicitly(
99
                RequirementsPexRequest(
100
                    (fs.address for fs in partition.field_sets),
101
                    hardcoded_interpreter_constraints=partition.interpreter_constraints,
102
                )
103
            )
104
        ),
105
        create_pex(
106
            pytype.to_pex_request(interpreter_constraints=partition.interpreter_constraints)
107
        ),
108
        find_config_file(pytype.config_request()),
109
    )
110

111
    input_digest = await merge_digests(
×
112
        MergeDigests((roots_sources.snapshot.digest, config_files.snapshot.digest))
113
    )
114

115
    runner = await create_venv_pex(
×
116
        VenvPexRequest(
117
            PexRequest(
118
                output_filename="pytype_runner.pex",
119
                interpreter_constraints=partition.interpreter_constraints,
120
                main=pytype.main,
121
                internal_only=True,
122
                pex_path=[pytype_pex, requirements_pex],
123
            ),
124
            pex_environment.in_sandbox(working_directory=None),
125
        ),
126
        **implicitly(),
127
    )
128

129
    result = await execute_process(
×
130
        **implicitly(
131
            VenvPexProcess(
132
                runner,
133
                argv=(
134
                    *(("--config", pytype.config) if pytype.config else ()),
135
                    "{pants_concurrency}",
136
                    *pytype.args,
137
                    *roots_sources.files,
138
                ),
139
                # This adds the venv/bin folder to PATH
140
                extra_env={
141
                    "PEX_VENV_BIN_PATH": "prepend",
142
                },
143
                input_digest=input_digest,
144
                output_files=roots_sources.files,
145
                concurrency_available=len(roots_sources.files),
146
                description=f"Run Pytype on {pluralize(len(roots_sources.files), 'file')}.",
147
                level=LogLevel.DEBUG,
148
            )
149
        )
150
    )
151

152
    return CheckResult.from_fallible_process_result(
×
153
        result,
154
        partition_description=partition.description(),
155
    )
156

157

UNCOV
158
@rule(
×
159
    desc="Determine if it is necessary to partition Pytype's input (interpreter_constraints and resolves)",
160
    level=LogLevel.DEBUG,
161
)
UNCOV
162
async def pytype_determine_partitions(
×
163
    request: PytypeRequest,
164
    pytype: Pytype,
165
    python_setup: PythonSetup,
166
) -> PytypePartitions:
167
    resolve_and_interpreter_constraints_to_field_sets = (
×
168
        _partition_by_interpreter_constraints_and_resolve(request.field_sets, python_setup)
169
    )
170

171
    coarsened_targets = await coarsened_targets_get(
×
172
        CoarsenedTargetsRequest(field_set.address for field_set in request.field_sets),
173
        **implicitly(),
174
    )
175
    coarsened_targets_by_address = coarsened_targets.by_address()
×
176

177
    return PytypePartitions(
×
178
        PytypePartition(
179
            FrozenOrderedSet(field_sets),
180
            CoarsenedTargets(
181
                OrderedSet(
182
                    coarsened_targets_by_address[field_set.address] for field_set in field_sets
183
                )
184
            ),
185
            resolve if len(python_setup.resolves) > 1 else None,
186
            interpreter_constraints or pytype.interpreter_constraints,
187
        )
188
        for (resolve, interpreter_constraints), field_sets in sorted(
189
            resolve_and_interpreter_constraints_to_field_sets.items()
190
        )
191
    )
192

193

UNCOV
194
@rule(desc="Typecheck using Pytype", level=LogLevel.DEBUG)
×
UNCOV
195
async def pytype_typecheck(
×
196
    request: PytypeRequest,
197
    pytype: Pytype,
198
) -> CheckResults:
199
    if pytype.skip:
×
200
        return CheckResults([], checker_name=request.tool_name)
×
201

202
    # Explicitly excluding `pytype` as a function argument to `pytype_determine_partitions` and `pytype_typecheck_partition`
203
    # as it throws "TypeError: unhashable type: 'Pytype'"
204
    partitions = await pytype_determine_partitions(request, **implicitly())
×
205
    partitioned_results = await concurrently(
×
206
        pytype_typecheck_partition(partition, **implicitly()) for partition in partitions
207
    )
208
    return CheckResults(
×
209
        partitioned_results,
210
        checker_name=request.tool_name,
211
    )
212

213

UNCOV
214
def rules() -> Iterable[Rule | UnionRule]:
×
UNCOV
215
    return (
×
216
        *collect_rules(),
217
        *config_files.rules(),
218
        *pex_from_targets.rules(),
219
        UnionRule(CheckRequest, PytypeRequest),
220
        UnionRule(ExportableTool, Pytype),
221
    )
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