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

pantsbuild / pants / 25259185675

02 May 2026 06:47PM UTC coverage: 92.141% (-0.8%) from 92.955%
25259185675

push

github

web-flow
Fix the dynamic UI. (#23306)

In #23114 we upgraded to indicatif 0.18.4,
which included a fix to respect TERM, and 
display nothing if it's unset.

Since we did not pass TERM through pantsd, the
dynamic ui is now not shown. 

This change fixes that, and also pass NO_COLOR
through, since indicatif inspects it too.

88773 of 96345 relevant lines covered (92.14%)

3.83 hits per line

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

90.98
/src/python/pants/backend/scala/compile/scalac_plugins.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
8✔
5

6
from collections.abc import Iterable, Iterator
8✔
7
from dataclasses import dataclass
8✔
8

9
from pants.backend.scala.subsystems.scalac import Scalac
8✔
10
from pants.backend.scala.target_types import (
8✔
11
    ScalaConsumedPluginNamesField,
12
    ScalacPluginArtifactField,
13
    ScalacPluginNameField,
14
)
15
from pants.build_graph.address import AddressInput
8✔
16
from pants.engine.addresses import Addresses
8✔
17
from pants.engine.environment import ChosenLocalEnvironmentName, EnvironmentName
8✔
18
from pants.engine.internals.build_files import resolve_address
8✔
19
from pants.engine.internals.graph import resolve_coarsened_targets as coarsened_targets_get
8✔
20
from pants.engine.internals.graph import resolve_target_parametrizations
8✔
21
from pants.engine.internals.native_engine import MergeDigests
8✔
22
from pants.engine.internals.parametrize import _TargetParametrizationsRequest
8✔
23
from pants.engine.intrinsics import merge_digests
8✔
24
from pants.engine.rules import collect_rules, concurrently, implicitly, rule
8✔
25
from pants.engine.target import (
8✔
26
    AllTargets,
27
    FieldDefaults,
28
    Target,
29
    Targets,
30
    TargetTypesToGenerateTargetsRequests,
31
    WrappedTarget,
32
)
33
from pants.jvm.compile import ClasspathEntry, FallibleClasspathEntry
8✔
34
from pants.jvm.goals import lockfile
8✔
35
from pants.jvm.resolve.coursier_fetch import CoursierFetchRequest, fetch_with_coursier
8✔
36
from pants.jvm.resolve.jvm_tool import rules as jvm_tool_rules
8✔
37
from pants.jvm.resolve.key import CoursierResolveKey
8✔
38
from pants.jvm.subsystems import JvmSubsystem
8✔
39
from pants.jvm.target_types import JvmResolveField
8✔
40
from pants.util.ordered_set import OrderedSet
8✔
41
from pants.util.strutil import bullet_list, softwrap
8✔
42

43

44
@dataclass(frozen=True)
8✔
45
class ScalaPluginsForTargetWithoutResolveRequest:
8✔
46
    target: Target
8✔
47

48

49
@dataclass(frozen=True)
8✔
50
class ScalaPluginsForTargetRequest:
8✔
51
    target: Target
8✔
52
    resolve_name: str
8✔
53

54

55
@dataclass(frozen=True)
8✔
56
class ScalaPluginTargetsForTarget:
8✔
57
    plugins: Targets
8✔
58
    artifacts: Targets
8✔
59

60

61
@dataclass(frozen=True)
8✔
62
class ScalaPluginsRequest:
8✔
63
    plugins: Targets
8✔
64
    artifacts: Targets
8✔
65
    resolve: CoursierResolveKey
8✔
66

67
    @classmethod
8✔
68
    def from_target_plugins(
8✔
69
        cls,
70
        seq: Iterable[ScalaPluginTargetsForTarget],
71
        resolve: CoursierResolveKey,
72
    ) -> ScalaPluginsRequest:
73
        plugins: OrderedSet[Target] = OrderedSet()
7✔
74
        artifacts: OrderedSet[Target] = OrderedSet()
7✔
75

76
        for spft in seq:
7✔
77
            plugins.update(spft.plugins)
7✔
78
            artifacts.update(spft.artifacts)
7✔
79

80
        return ScalaPluginsRequest(Targets(plugins), Targets(artifacts), resolve)
7✔
81

82

83
@dataclass(frozen=True)
8✔
84
class ScalaPlugins:
8✔
85
    names: tuple[str, ...]
8✔
86
    classpath: ClasspathEntry
8✔
87

88
    def args(self, prefix: str | None = None) -> Iterator[str]:
8✔
89
        p = f"{prefix}/" if prefix else ""
7✔
90
        for scalac_plugin_path in self.classpath.filenames:
7✔
91
            yield f"-Xplugin:{p}{scalac_plugin_path}"
1✔
92
        for name in self.names:
7✔
93
            yield f"-Xplugin-require:{name}"
1✔
94

95

96
class AllScalaPluginTargets(Targets):
8✔
97
    pass
8✔
98

99

100
@rule
8✔
101
async def all_scala_plugin_targets(targets: AllTargets) -> AllScalaPluginTargets:
8✔
102
    return AllScalaPluginTargets(
7✔
103
        tgt for tgt in targets if tgt.has_fields((ScalacPluginArtifactField, ScalacPluginNameField))
104
    )
105

106

107
@rule
8✔
108
async def add_resolve_name_to_plugin_request(
8✔
109
    request: ScalaPluginsForTargetWithoutResolveRequest, jvm: JvmSubsystem
110
) -> ScalaPluginsForTargetRequest:
111
    return ScalaPluginsForTargetRequest(
×
112
        request.target, request.target[JvmResolveField].normalized_value(jvm)
113
    )
114

115

116
async def _resolve_scalac_plugin_artifact(
8✔
117
    field: ScalacPluginArtifactField,
118
    consumer_target: Target,
119
    target_types_to_generate_requests: TargetTypesToGenerateTargetsRequests,
120
    local_environment_name: ChosenLocalEnvironmentName,
121
    field_defaults: FieldDefaults,
122
) -> WrappedTarget:
123
    """Helps resolve the actual artifact for a scalac plugin even in the scenario in which the
124
    artifact has been declared as a scala_artifact and it has been parametrized (i.e. across
125
    multiple resolves for cross building)."""
126

127
    environment_name = local_environment_name.val
1✔
128

129
    address = await resolve_address(**implicitly({field.to_address_input(): AddressInput}))
1✔
130

131
    parametrizations = await resolve_target_parametrizations(
1✔
132
        **implicitly(
133
            {
134
                _TargetParametrizationsRequest(
135
                    address.maybe_convert_to_target_generator(),
136
                    description_of_origin=(
137
                        f"the target generator {address.maybe_convert_to_target_generator()}"
138
                    ),
139
                ): _TargetParametrizationsRequest,
140
                environment_name: EnvironmentName,
141
            }
142
        )
143
    )
144

145
    target = parametrizations.get_subset(
1✔
146
        address, consumer_target, field_defaults, target_types_to_generate_requests
147
    )
148
    if (
1✔
149
        target_types_to_generate_requests.is_generator(target)
150
        and not target.address.is_generated_target
151
    ):
152
        generated_tgts = list(parametrizations.generated_for(target.address).values())
×
153
        if len(generated_tgts) > 1:
×
154
            raise Exception(
×
155
                softwrap(
156
                    f"""
157
                    Could not resolve scalac plugin artifact {address} from target {field.address}
158
                    as it points to a target generator that produced more than one target:
159

160
                    {bullet_list([tgt.address.spec for tgt in generated_tgts])}
161
                    """
162
                )
163
            )
164
        if len(generated_tgts) == 1:
×
165
            target = generated_tgts[0]
×
166

167
    return WrappedTarget(target)
1✔
168

169

170
@rule
8✔
171
async def resolve_scala_plugins_for_target(
8✔
172
    request: ScalaPluginsForTargetRequest,
173
    all_scala_plugins: AllScalaPluginTargets,
174
    jvm: JvmSubsystem,
175
    scalac: Scalac,
176
    target_types_to_generate_requests: TargetTypesToGenerateTargetsRequests,
177
    local_environment_name: ChosenLocalEnvironmentName,
178
    field_defaults: FieldDefaults,
179
) -> ScalaPluginTargetsForTarget:
180
    target = request.target
7✔
181
    resolve = request.resolve_name
7✔
182

183
    plugin_names = target.get(ScalaConsumedPluginNamesField).value
7✔
184
    if plugin_names is None:
7✔
185
        plugin_names_by_resolve = scalac.parsed_default_plugins()
6✔
186
        plugin_names = tuple(plugin_names_by_resolve.get(resolve, ()))
6✔
187

188
    candidate_plugins = []
7✔
189
    candidate_artifacts = []
7✔
190
    for plugin in all_scala_plugins:
7✔
191
        if _plugin_name(plugin) not in plugin_names:
1✔
192
            continue
×
193
        candidate_plugins.append(plugin)
1✔
194
        artifact_field = plugin[ScalacPluginArtifactField]
1✔
195
        wrapped_target = await _resolve_scalac_plugin_artifact(
1✔
196
            artifact_field,
197
            request.target,
198
            target_types_to_generate_requests,
199
            local_environment_name,
200
            field_defaults,
201
        )
202
        candidate_artifacts.append(wrapped_target.target)
1✔
203

204
    plugins: dict[str, tuple[Target, Target]] = {}  # Maps plugin name to relevant JVM artifact
7✔
205
    for plugin, artifact in zip(candidate_plugins, candidate_artifacts):
7✔
206
        if artifact[JvmResolveField].normalized_value(jvm) != resolve:
1✔
207
            continue
×
208

209
        plugins[_plugin_name(plugin)] = (plugin, artifact)
1✔
210

211
    for plugin_name in plugin_names:
7✔
212
        if plugin_name not in plugins:
1✔
213
            raise Exception(
×
214
                f"Could not find Scala plugin `{plugin_name}` in resolve `{resolve}` "
215
                f"for target {request.target.address}."
216
            )
217

218
    plugin_targets, artifact_targets = zip(*plugins.values()) if plugins else ((), ())
7✔
219
    return ScalaPluginTargetsForTarget(Targets(plugin_targets), Targets(artifact_targets))
7✔
220

221

222
def _plugin_name(target: Target) -> str:
8✔
223
    return target[ScalacPluginNameField].value or target.address.target_name
1✔
224

225

226
@rule
8✔
227
async def fetch_plugins(request: ScalaPluginsRequest) -> ScalaPlugins:
8✔
228
    # Fetch all the artifacts
229
    coarsened_targets = await coarsened_targets_get(
7✔
230
        **implicitly(Addresses(target.address for target in request.artifacts))
231
    )
232
    fallible_artifacts = await concurrently(
7✔
233
        fetch_with_coursier(CoursierFetchRequest(ct, resolve=request.resolve))
234
        for ct in coarsened_targets
235
    )
236

237
    artifacts = FallibleClasspathEntry.if_all_succeeded(fallible_artifacts)
7✔
238
    if artifacts is None:
7✔
239
        failed = [i for i in fallible_artifacts if i.exit_code != 0]
×
240
        raise Exception(f"Fetching local scala plugins failed: {failed}")
×
241

242
    merged_classpath_digest = await merge_digests(MergeDigests(i.digest for i in artifacts))
7✔
243
    merged = ClasspathEntry.merge(merged_classpath_digest, artifacts)
7✔
244

245
    names = tuple(_plugin_name(target) for target in request.plugins)
7✔
246

247
    return ScalaPlugins(names=names, classpath=merged)
7✔
248

249

250
def rules():
8✔
251
    return (
8✔
252
        *collect_rules(),
253
        *jvm_tool_rules(),
254
        *lockfile.rules(),
255
    )
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