• 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/project_info/dependencies.py
1
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
UNCOV
3
import itertools
×
UNCOV
4
import json
×
UNCOV
5
from enum import Enum
×
6

UNCOV
7
from pants.engine.addresses import Addresses
×
UNCOV
8
from pants.engine.console import Console
×
UNCOV
9
from pants.engine.goal import Goal, GoalSubsystem, LineOriented
×
UNCOV
10
from pants.engine.internals.graph import resolve_targets, resolve_unexpanded_targets
×
UNCOV
11
from pants.engine.internals.graph import transitive_targets as transitive_targets_get
×
UNCOV
12
from pants.engine.rules import collect_rules, concurrently, goal_rule, implicitly
×
UNCOV
13
from pants.engine.target import AlwaysTraverseDeps
×
UNCOV
14
from pants.engine.target import Dependencies as DependenciesField
×
UNCOV
15
from pants.engine.target import DependenciesRequest, Targets, TransitiveTargetsRequest
×
UNCOV
16
from pants.option.option_types import BoolOption, EnumOption
×
17

18

UNCOV
19
class DependenciesOutputFormat(Enum):
×
20
    """Output format for listing dependencies.
21

22
    text: List all dependencies as a single list of targets in plain text.
23
    json: List all dependencies as a mapping `{target: [dependencies]}`.
24
    """
25

UNCOV
26
    text = "text"
×
UNCOV
27
    json = "json"
×
28

29

UNCOV
30
class DependenciesSubsystem(LineOriented, GoalSubsystem):
×
UNCOV
31
    name = "dependencies"
×
UNCOV
32
    help = "List the dependencies of the input files/targets."
×
33

UNCOV
34
    transitive = BoolOption(
×
35
        default=False,
36
        help="List all transitive dependencies. If unspecified, list direct dependencies only.",
37
    )
UNCOV
38
    closed = BoolOption(
×
39
        default=False,
40
        help="Include the input targets in the output, along with the dependencies.",
41
    )
UNCOV
42
    format = EnumOption(
×
43
        default=DependenciesOutputFormat.text,
44
        help="Output format for listing dependencies.",
45
    )
46

47

UNCOV
48
class Dependencies(Goal):
×
UNCOV
49
    subsystem_cls = DependenciesSubsystem
×
UNCOV
50
    environment_behavior = Goal.EnvironmentBehavior.LOCAL_ONLY
×
51

52

UNCOV
53
async def list_dependencies_as_json(
×
54
    addresses: Addresses, dependencies_subsystem: DependenciesSubsystem, console: Console
55
) -> None:
56
    """Get dependencies for given addresses and list them in the console in JSON."""
57
    # NB: We must preserve target generators for the roots, i.e. not replace with their
58
    # generated targets.
59
    target_roots = await resolve_unexpanded_targets(addresses)
×
60
    # NB: When determining dependencies, though, we replace target generators with their
61
    # generated targets.
62
    if dependencies_subsystem.transitive:
×
63
        transitive_targets_group = await concurrently(
×
64
            transitive_targets_get(
65
                TransitiveTargetsRequest(
66
                    (address,), should_traverse_deps_predicate=AlwaysTraverseDeps()
67
                ),
68
                **implicitly(),
69
            )
70
            for address in addresses
71
        )
72

73
        iterated_targets = []
×
74
        for idx, transitive_targets in enumerate(transitive_targets_group):
×
75
            targets_collection = {
×
76
                str(tgt.address)
77
                for tgt in (
78
                    transitive_targets.closure
79
                    if dependencies_subsystem.closed
80
                    else transitive_targets.dependencies
81
                )
82
            }
83
            iterated_targets.append(sorted(targets_collection))
×
84

85
    else:
86
        dependencies_per_target_root = await concurrently(
×
87
            resolve_targets(
88
                **implicitly(
89
                    DependenciesRequest(
90
                        tgt.get(DependenciesField),
91
                        should_traverse_deps_predicate=AlwaysTraverseDeps(),
92
                    )
93
                )
94
            )
95
            for tgt in target_roots
96
        )
97

98
        iterated_targets = []
×
99
        for idx, targets in enumerate(dependencies_per_target_root):
×
100
            targets_collection = {str(tgt.address) for tgt in targets}
×
101
            if dependencies_subsystem.closed:
×
102
                targets_collection.add(str(target_roots[idx].address))
×
103
            iterated_targets.append(sorted(targets_collection))
×
104

105
    # The assumption is that when iterating the targets and sending dependency requests
106
    # for them, the lists of dependencies are returned in the very same order.
107
    mapping = dict(zip([str(tgt.address) for tgt in target_roots], iterated_targets))
×
108
    output = json.dumps(mapping, indent=4)
×
109

110
    with dependencies_subsystem.line_oriented(console) as print_stdout:
×
111
        print_stdout(output)
×
112

113

UNCOV
114
async def list_dependencies_as_plain_text(
×
115
    addresses: Addresses, dependencies_subsystem: DependenciesSubsystem, console: Console
116
) -> None:
117
    """Get dependencies for given addresses and list them in the console as a single list."""
118
    if dependencies_subsystem.transitive:
×
119
        transitive_targets = await transitive_targets_get(
×
120
            TransitiveTargetsRequest(
121
                addresses, should_traverse_deps_predicate=AlwaysTraverseDeps()
122
            ),
123
            **implicitly(),
124
        )
125
        targets = Targets(transitive_targets.dependencies)
×
126
    else:
127
        # NB: We must preserve target generators for the roots, i.e. not replace with their
128
        # generated targets.
129
        target_roots = await resolve_unexpanded_targets(addresses)
×
130
        # NB: When determining dependencies, though, we replace target generators with their
131
        # generated targets.
132
        dependencies_per_target_root = await concurrently(
×
133
            resolve_targets(
134
                **implicitly(
135
                    DependenciesRequest(
136
                        tgt.get(DependenciesField),
137
                        should_traverse_deps_predicate=AlwaysTraverseDeps(),
138
                    )
139
                )
140
            )
141
            for tgt in target_roots
142
        )
143
        targets = Targets(itertools.chain.from_iterable(dependencies_per_target_root))
×
144

145
    address_strings = {addr.spec for addr in addresses} if dependencies_subsystem.closed else set()
×
146
    for tgt in targets:
×
147
        address_strings.add(tgt.address.spec)
×
148

149
    with dependencies_subsystem.line_oriented(console) as print_stdout:
×
150
        for address in sorted(address_strings):
×
151
            print_stdout(address)
×
152

153

UNCOV
154
@goal_rule
×
UNCOV
155
async def dependencies(
×
156
    console: Console, addresses: Addresses, dependencies_subsystem: DependenciesSubsystem
157
) -> Dependencies:
158
    if DependenciesOutputFormat.text == dependencies_subsystem.format:
×
159
        await list_dependencies_as_plain_text(
×
160
            addresses=addresses,
161
            dependencies_subsystem=dependencies_subsystem,
162
            console=console,
163
        )
164

165
    elif DependenciesOutputFormat.json == dependencies_subsystem.format:
×
166
        await list_dependencies_as_json(
×
167
            addresses=addresses,
168
            dependencies_subsystem=dependencies_subsystem,
169
            console=console,
170
        )
171

172
    return Dependencies(exit_code=0)
×
173

174

UNCOV
175
def rules():
×
UNCOV
176
    return collect_rules()
×
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