1
1
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
2
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
-
3
+ import json
4
4
from collections import defaultdict
5
5
from dataclasses import dataclass
6
+ from enum import Enum
6
7
from typing import Iterable , Set
7
8
8
9
from pants .engine .addresses import Address , Addresses
16
17
Dependencies ,
17
18
DependenciesRequest ,
18
19
)
19
- from pants .option .option_types import BoolOption
20
+ from pants .option .option_types import BoolOption , EnumOption
20
21
from pants .util .frozendict import FrozenDict
21
22
from pants .util .logging import LogLevel
22
23
from pants .util .ordered_set import FrozenOrderedSet
@@ -27,6 +28,17 @@ class AddressToDependents:
27
28
mapping : FrozenDict [Address , FrozenOrderedSet [Address ]]
28
29
29
30
31
+ class DependentsOutputFormat (Enum ):
32
+ """Output format for listing dependents.
33
+
34
+ text: List all dependents as a single list of targets in plain text.
35
+ json: List all dependents as a mapping `{target: [dependents]}`.
36
+ """
37
+
38
+ text = "text"
39
+ json = "json"
40
+
41
+
30
42
@rule (desc = "Map all targets to their dependents" , level = LogLevel .DEBUG )
31
43
async def map_addresses_to_dependents (all_targets : AllUnexpandedTargets ) -> AddressToDependents :
32
44
dependencies_per_target = await MultiGet (
@@ -105,7 +117,12 @@ class DependentsSubsystem(LineOriented, GoalSubsystem):
105
117
)
106
118
closed = BoolOption (
107
119
default = False ,
108
- help = "Include the input targets in the output, along with the dependents." ,
120
+ help = "Include the input targets in the output, along with the dependents. This option "
121
+ "only applies when using the `text` format." ,
122
+ )
123
+ format = EnumOption (
124
+ default = DependentsOutputFormat .text ,
125
+ help = "Output format for listing dependents." ,
109
126
)
110
127
111
128
@@ -114,21 +131,66 @@ class DependentsGoal(Goal):
114
131
environment_behavior = Goal .EnvironmentBehavior .LOCAL_ONLY
115
132
116
133
117
- @ goal_rule
118
- async def dependents_goal (
119
- specified_addresses : Addresses , dependents_subsystem : DependentsSubsystem , console : Console
120
- ) -> DependentsGoal :
134
+ async def list_dependents_as_plain_text (
135
+ addresses : Addresses , dependents_subsystem : DependentsSubsystem , console : Console
136
+ ) -> None :
137
+ """Get dependents for given addresses and list them in the console as a single list."""
121
138
dependents = await Get (
122
139
Dependents ,
123
140
DependentsRequest (
124
- specified_addresses ,
141
+ addresses ,
125
142
transitive = dependents_subsystem .transitive ,
126
143
include_roots = dependents_subsystem .closed ,
127
144
),
128
145
)
129
146
with dependents_subsystem .line_oriented (console ) as print_stdout :
130
147
for address in dependents :
131
148
print_stdout (address .spec )
149
+
150
+
151
+ async def list_dependents_as_json (
152
+ addresses : Addresses , dependents_subsystem : DependentsSubsystem , console : Console
153
+ ) -> None :
154
+ """Get dependents for given addresses and list them in the console in JSON.
155
+
156
+ Note that `--closed` option is ignored as it doesn't make sense to duplicate source address in
157
+ the list of its dependents.
158
+ """
159
+ dependents_group = await MultiGet (
160
+ Get (
161
+ Dependents ,
162
+ DependentsRequest (
163
+ (address ,),
164
+ transitive = dependents_subsystem .transitive ,
165
+ include_roots = False ,
166
+ ),
167
+ )
168
+ for address in addresses
169
+ )
170
+ iterated_addresses = []
171
+ for dependents in dependents_group :
172
+ iterated_addresses .append (sorted ([str (address ) for address in dependents ]))
173
+ mapping = dict (zip ([str (address ) for address in addresses ], iterated_addresses ))
174
+ output = json .dumps (mapping , indent = 4 )
175
+ console .print_stdout (output )
176
+
177
+
178
+ @goal_rule
179
+ async def dependents_goal (
180
+ specified_addresses : Addresses , dependents_subsystem : DependentsSubsystem , console : Console
181
+ ) -> DependentsGoal :
182
+ if DependentsOutputFormat .text == dependents_subsystem .format :
183
+ await list_dependents_as_plain_text (
184
+ addresses = specified_addresses ,
185
+ dependents_subsystem = dependents_subsystem ,
186
+ console = console ,
187
+ )
188
+ elif DependentsOutputFormat .json == dependents_subsystem .format :
189
+ await list_dependents_as_json (
190
+ addresses = specified_addresses ,
191
+ dependents_subsystem = dependents_subsystem ,
192
+ console = console ,
193
+ )
132
194
return DependentsGoal (exit_code = 0 )
133
195
134
196
0 commit comments