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

pantsbuild / pants / 25441711719

06 May 2026 02:31PM UTC coverage: 92.915%. Remained the same
25441711719

push

github

web-flow
use sha pin (with comment) format for generated actions (#23312)

Per the GitHub Action best practices we recently enabled at #23249, we
should pin each action to a SHA so that the reference is actually
immutable.

This will -- I hope -- knock out a large chunk of the 421 alerts we
currently get from zizmor. The next followup would then be upgrades and
harmonizing the generated and none-generated pins.

Notice: This idea was suggested by Claude while going over pinact output
and I was surprised to see that post processing the yaml wasn't too
gross.

92206 of 99237 relevant lines covered (92.91%)

4.04 hits per line

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

76.32
/src/python/pants/backend/python/goals/debug_goals.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
import json
1✔
7
from dataclasses import dataclass
1✔
8
from enum import Enum
1✔
9
from typing import Any
1✔
10

11
from pants.backend.project_info.peek import _PeekJsonEncoder
1✔
12
from pants.backend.python.dependency_inference.module_mapper import ResolveName
1✔
13
from pants.backend.python.dependency_inference.parse_python_dependencies import (
1✔
14
    ParsedPythonImportInfo,
15
    PythonFileDependencies,
16
)
17
from pants.backend.python.dependency_inference.rules import (
1✔
18
    ImportResolveResult,
19
    PythonImportDependenciesInferenceFieldSet,
20
    ResolvedParsedPythonDependencies,
21
    ResolvedParsedPythonDependenciesRequest,
22
    UnownedImportsPossibleOwners,
23
    UnownedImportsPossibleOwnersRequest,
24
    _collect_imports_info,
25
    _exec_parse_deps,
26
    _find_other_owners_for_unowned_imports,
27
    import_rules,
28
    resolve_parsed_dependencies,
29
)
30
from pants.backend.python.goals.run_python_source import PythonSourceFieldSet
1✔
31
from pants.backend.python.subsystems.setup import PythonSetup
1✔
32
from pants.build_graph.address import Address
1✔
33
from pants.engine.console import Console
1✔
34
from pants.engine.goal import Goal, GoalSubsystem
1✔
35
from pants.engine.internals.selectors import concurrently
1✔
36
from pants.engine.rules import collect_rules, goal_rule, implicitly, rule
1✔
37
from pants.engine.target import Targets
1✔
38
from pants.option.option_types import EnumOption
1✔
39
from pants.util.strutil import softwrap
1✔
40

41

42
class AnalysisFlavor(Enum):
1✔
43
    raw_dependency_inference = "raw_dependency_inference"
1✔
44
    dependency_inference = "dependency_inference"
1✔
45

46

47
class DumpPythonSourceAnalysisSubsystem(GoalSubsystem):
1✔
48
    name = "python-dump-source-analysis"
1✔
49
    help = "Dump source analysis for python_source targets."
1✔
50

51
    flavor = EnumOption(
1✔
52
        "--analysis-flavor",
53
        default=AnalysisFlavor.dependency_inference,
54
        help=softwrap(
55
            f"""\
56
            The type of information that should be returned.\n
57
            * `{AnalysisFlavor.dependency_inference.value}`: The results of dependency inference, for every detected import in every file.\n
58
            * `{AnalysisFlavor.raw_dependency_inference.value}`: The raw intermediate results of the dependency inference process,
59
            at every stage they're available.
60
            Potentially useful for debugging the dependency inference process.\n
61
            """
62
        ),
63
    )
64

65

66
class DumpPythonSourceAnalysis(Goal):
1✔
67
    subsystem_cls = DumpPythonSourceAnalysisSubsystem
1✔
68
    environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY  # TODO(#17129) — Migrate this.
1✔
69

70

71
@dataclass(frozen=True)
1✔
72
class PythonSourceAnalysis:
1✔
73
    """Information on the inferred imports for a Python file, including all raw intermediate
74
    results."""
75

76
    fs: PythonImportDependenciesInferenceFieldSet
1✔
77
    identified: PythonFileDependencies
1✔
78
    resolved: ResolvedParsedPythonDependencies
1✔
79
    possible_owners: UnownedImportsPossibleOwners
1✔
80

81

82
@rule
1✔
83
async def dump_python_source_analysis_single(
1✔
84
    fs: PythonImportDependenciesInferenceFieldSet,
85
    python_setup: PythonSetup,
86
) -> PythonSourceAnalysis:
87
    """Infer the dependencies for a single python fieldset, keeping all the intermediate results."""
88

89
    parsed_dependencies = await _exec_parse_deps(fs, python_setup)
1✔
90

91
    resolve = fs.resolve.normalized_value(python_setup)
1✔
92

93
    resolved_dependencies = await resolve_parsed_dependencies(
1✔
94
        ResolvedParsedPythonDependenciesRequest(fs, parsed_dependencies, resolve), **implicitly()
95
    )
96

97
    import_deps, unowned_imports = _collect_imports_info(resolved_dependencies.resolve_results)
1✔
98

99
    imports_to_other_owners = await _find_other_owners_for_unowned_imports(
1✔
100
        UnownedImportsPossibleOwnersRequest(unowned_imports, resolve),
101
    )
102

103
    return PythonSourceAnalysis(
1✔
104
        fs, parsed_dependencies, resolved_dependencies, imports_to_other_owners
105
    )
106

107

108
@dataclass(frozen=True)
1✔
109
class ImportAnalysis:
1✔
110
    """Information on the inferred imports for a Python file."""
111

112
    name: str
1✔
113
    reference: ParsedPythonImportInfo | str
1✔
114
    resolved: ImportResolveResult
1✔
115
    possible_resolve: list[tuple[Address, ResolveName]] | None
1✔
116

117

118
@dataclass(frozen=True)
1✔
119
class CollectedImportAnalysis:
1✔
120
    """Collected information on all Python files."""
121

122
    imports: list[ImportAnalysis]
1✔
123
    assets: list[ImportAnalysis]
1✔
124

125

126
def collect_analysis(raw: PythonSourceAnalysis) -> CollectedImportAnalysis:
1✔
127
    """Collect raw analysis and present it in a helpful per-import format."""
128
    imports = []
×
129

130
    resolved_results = raw.resolved.resolve_results
×
131

132
    for name, info in raw.identified.imports.items():
×
133
        possible_resolve = raw.possible_owners.value.get(name)
×
134

135
        imports.append(
×
136
            ImportAnalysis(
137
                name=name,
138
                reference=info,
139
                resolved=resolved_results[name],
140
                possible_resolve=possible_resolve,
141
            )
142
        )
143

144
    assets = []
×
145
    resolved_assets = raw.resolved.assets
×
146

147
    for name in raw.identified.assets:
×
148
        possible_resolve = raw.possible_owners.value.get(name)
×
149

150
        assets.append(
×
151
            ImportAnalysis(
152
                name=name,
153
                reference=name,  # currently assets don't keep track of their line numbers
154
                resolved=resolved_assets[name],
155
                possible_resolve=possible_resolve,
156
            )
157
        )
158

159
    return CollectedImportAnalysis(imports, assets)
×
160

161

162
@goal_rule
1✔
163
async def dump_python_source_analysis(
1✔
164
    request: DumpPythonSourceAnalysisSubsystem,
165
    targets: Targets,
166
    console: Console,
167
) -> DumpPythonSourceAnalysis:
168
    source_field_sets = [
×
169
        PythonImportDependenciesInferenceFieldSet.create(tgt)
170
        for tgt in targets
171
        if PythonSourceFieldSet.is_applicable(tgt)
172
    ]
173

174
    source_analysis = await concurrently(
×
175
        dump_python_source_analysis_single(fs, **implicitly()) for fs in source_field_sets
176
    )
177

178
    output: Any
179
    if request.flavor == AnalysisFlavor.raw_dependency_inference:
×
180
        output = source_analysis
×
181
    else:
182
        output = {str(a.fs.address): collect_analysis(a) for a in source_analysis}
×
183

184
    console.print_stdout(json.dumps(output, cls=_PeekJsonEncoder))
×
185
    return DumpPythonSourceAnalysis(exit_code=0)
×
186

187

188
def rules():
1✔
189
    return [
1✔
190
        *import_rules(),
191
        *collect_rules(),
192
    ]
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

© 2026 Coveralls, Inc