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

pantsbuild / pants / 25443604553

06 May 2026 03:05PM UTC coverage: 92.879% (-0.04%) from 92.915%
25443604553

push

github

web-flow
[pants_ng] Scaffolding for a pants_ng mode. (#23319)

In this mode the command line is parsed as an
NG invocation, and dispatched appropriately.

Of course at the moment there are no
implementations to dispatch to. That will follow.

This does expose a new option, `pants_ng` to users. 
There is a big warning not to set it, but we're not trying
to hide that we're working on a new thing, so I am
comfortable with this.

25 of 76 new or added lines in 9 files covered. (32.89%)

1294 existing lines in 76 files now uncovered.

92234 of 99306 relevant lines covered (92.88%)

4.05 hits per line

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

95.83
/src/python/pants/jvm/goals/lockfile.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
12✔
5

6
from collections import defaultdict
12✔
7
from collections.abc import Coroutine, Mapping
12✔
8
from dataclasses import dataclass
12✔
9
from typing import Any
12✔
10

11
from pants.core.goals.generate_lockfiles import (
12✔
12
    DEFAULT_TOOL_LOCKFILE,
13
    GenerateLockfile,
14
    GenerateLockfileResult,
15
    GenerateLockfilesSubsystem,
16
    KnownUserResolveNames,
17
    KnownUserResolveNamesRequest,
18
    RequestedUserResolveNames,
19
    UserGenerateLockfiles,
20
)
21
from pants.core.goals.resolves import ExportableTool
12✔
22
from pants.engine.environment import EnvironmentName
12✔
23
from pants.engine.fs import CreateDigest, FileContent
12✔
24
from pants.engine.internals.selectors import concurrently
12✔
25
from pants.engine.intrinsics import create_digest
12✔
26
from pants.engine.rules import collect_rules, implicitly, rule
12✔
27
from pants.engine.target import AllTargets
12✔
28
from pants.engine.unions import UnionMembership, UnionRule, union
12✔
29
from pants.jvm.resolve import coursier_fetch
12✔
30
from pants.jvm.resolve.common import (
12✔
31
    ArtifactRequirement,
32
    ArtifactRequirements,
33
    GatherJvmCoordinatesRequest,
34
)
35
from pants.jvm.resolve.coursier_fetch import coursier_resolve_lockfile
12✔
36
from pants.jvm.resolve.jvm_tool import (
12✔
37
    GenerateJvmLockfileFromTool,
38
    JvmToolBase,
39
    gather_coordinates_for_jvm_lockfile,
40
)
41
from pants.jvm.resolve.lockfile_metadata import JVMLockfileMetadata
12✔
42
from pants.jvm.subsystems import JvmSubsystem
12✔
43
from pants.jvm.target_types import JvmArtifactResolveField, JvmResolveField
12✔
44
from pants.option.subsystem import _construct_subsystem
12✔
45
from pants.util.docutil import bin_name
12✔
46
from pants.util.logging import LogLevel
12✔
47
from pants.util.ordered_set import OrderedSet
12✔
48

49

50
@dataclass(frozen=True)
12✔
51
class GenerateJvmLockfile(GenerateLockfile):
12✔
52
    artifacts: ArtifactRequirements
12✔
53

54

55
@union(in_scope_types=[EnvironmentName])
12✔
56
@dataclass(frozen=True)
12✔
57
class ValidateJvmArtifactsForResolveRequest:
12✔
58
    """Hook for backends to validate the artifact requirements requested for a resolve.
59

60
    The main user is the Scala backend which will ensure scala-library is present in the resolve.
61
    """
62

63
    artifacts: ArtifactRequirements
12✔
64
    resolve_name: str
12✔
65

66

67
@dataclass(frozen=True)
12✔
68
class ValidateJvmArtifactsForResolveResult:
12✔
69
    """Sentinel type that represents that a backend is satisfied with the artifacts for a JVM
70
    resolve."""
71

72

73
@rule(polymorphic=True)
12✔
74
async def _validate_jvm_artifacts_for_resolve(
12✔
75
    req: ValidateJvmArtifactsForResolveRequest, env_name: EnvironmentName
76
) -> ValidateJvmArtifactsForResolveResult:
77
    raise NotImplementedError()
×
78

79

80
@rule(desc="Generate JVM lockfile", level=LogLevel.DEBUG)
12✔
81
async def generate_jvm_lockfile(
12✔
82
    request: GenerateJvmLockfile,
83
    generate_lockfiles_subsystem: GenerateLockfilesSubsystem,
84
) -> GenerateLockfileResult:
UNCOV
85
    resolved_lockfile = await coursier_resolve_lockfile(request.artifacts)
1✔
UNCOV
86
    regenerate_command = (
1✔
87
        generate_lockfiles_subsystem.custom_command or f"{bin_name()} generate-lockfiles"
88
    )
89

UNCOV
90
    resolved_lockfile_contents = resolved_lockfile.to_serialized()
1✔
UNCOV
91
    metadata = JVMLockfileMetadata.new(request.artifacts)
1✔
UNCOV
92
    resolved_lockfile_contents = metadata.add_header_to_lockfile(
1✔
93
        resolved_lockfile_contents,
94
        regenerate_command=regenerate_command,
95
        delimeter="#",
96
    )
97

UNCOV
98
    lockfile_digest = await create_digest(
1✔
99
        CreateDigest([FileContent(request.lockfile_dest, resolved_lockfile_contents)])
100
    )
UNCOV
101
    return GenerateLockfileResult(lockfile_digest, request.resolve_name, request.lockfile_dest)
1✔
102

103

104
class RequestedJVMUserResolveNames(RequestedUserResolveNames):
12✔
105
    pass
12✔
106

107

108
class KnownJVMUserResolveNamesRequest(KnownUserResolveNamesRequest):
12✔
109
    pass
12✔
110

111

112
@rule
12✔
113
async def determine_jvm_user_resolves(
12✔
114
    _: KnownJVMUserResolveNamesRequest,
115
    jvm_subsystem: JvmSubsystem,
116
    union_membership: UnionMembership,
117
) -> KnownUserResolveNames:
118
    jvm_tool_resolves = ExportableTool.filter_for_subclasses(union_membership, JvmToolBase)
×
119
    names = (*jvm_subsystem.resolves.keys(), *jvm_tool_resolves.keys())
×
120
    return KnownUserResolveNames(
×
121
        names=names,
122
        option_name=f"[{jvm_subsystem.options_scope}].resolves",
123
        requested_resolve_names_cls=RequestedJVMUserResolveNames,
124
    )
125

126

127
@dataclass(frozen=True)
12✔
128
class _ValidateJvmArtifactsRequest:
12✔
129
    artifacts: ArtifactRequirements
12✔
130
    resolve_name: str
12✔
131

132

133
@rule
12✔
134
async def validate_jvm_artifacts_for_resolve(
12✔
135
    request: _ValidateJvmArtifactsRequest,
136
    union_membership: UnionMembership,
137
    jvm_subsystem: JvmSubsystem,
138
) -> GenerateJvmLockfile:
139
    impls = union_membership.get(ValidateJvmArtifactsForResolveRequest)
2✔
140
    for impl in impls:
2✔
141
        validate_request = impl(artifacts=request.artifacts, resolve_name=request.resolve_name)
1✔
142
        _ = await _validate_jvm_artifacts_for_resolve(
1✔
143
            **implicitly({validate_request: ValidateJvmArtifactsForResolveRequest})
144
        )
145

UNCOV
146
    return GenerateJvmLockfile(
1✔
147
        artifacts=request.artifacts,
148
        resolve_name=request.resolve_name,
149
        lockfile_dest=jvm_subsystem.resolves[request.resolve_name],
150
        diff=False,
151
    )
152

153

154
@rule
12✔
155
async def setup_lockfile_request_from_tool(
12✔
156
    request: GenerateJvmLockfileFromTool,
157
) -> GenerateJvmLockfile:
158
    artifacts = await gather_coordinates_for_jvm_lockfile(
2✔
159
        GatherJvmCoordinatesRequest(request.artifact_inputs, request.artifact_option_name)
160
    )
161
    return GenerateJvmLockfile(
2✔
162
        artifacts=artifacts,
163
        resolve_name=request.resolve_name,
164
        lockfile_dest=(
165
            request.lockfile if request.lockfile != DEFAULT_TOOL_LOCKFILE else DEFAULT_TOOL_LOCKFILE
166
        ),
167
        diff=False,
168
    )
169

170

171
async def _plan_generate_lockfile(
12✔
172
    resolve, resolve_to_artifacts, tools
173
) -> Coroutine[Any, Any, GenerateJvmLockfile]:
174
    """Generate a JVM lockfile request for each requested resolve.
175

176
    This step also allows other backends to validate the proposed set of artifact requirements for
177
    each resolve.
178
    """
179
    if resolve in resolve_to_artifacts:
2✔
180
        return validate_jvm_artifacts_for_resolve(
2✔
181
            _ValidateJvmArtifactsRequest(
182
                artifacts=ArtifactRequirements(resolve_to_artifacts[resolve]),
183
                resolve_name=resolve,
184
            ),
185
            **implicitly(),
186
        )
187
    elif resolve in tools:
2✔
UNCOV
188
        tool_cls: type[JvmToolBase] = tools[resolve]
1✔
UNCOV
189
        tool = await _construct_subsystem(tool_cls)
1✔
190

UNCOV
191
        return setup_lockfile_request_from_tool(
1✔
192
            GenerateJvmLockfileFromTool.create(tool),
193
        )
194

195
    else:
196
        return validate_jvm_artifacts_for_resolve(
1✔
197
            _ValidateJvmArtifactsRequest(
198
                artifacts=ArtifactRequirements(()),
199
                resolve_name=resolve,
200
            ),
201
            **implicitly(),
202
        )
203

204

205
@rule
12✔
206
async def setup_user_lockfile_requests(
12✔
207
    requested: RequestedJVMUserResolveNames,
208
    all_targets: AllTargets,
209
    jvm_subsystem: JvmSubsystem,
210
    union_membership: UnionMembership,
211
) -> UserGenerateLockfiles:
212
    resolve_to_artifacts: Mapping[str, OrderedSet[ArtifactRequirement]] = defaultdict(OrderedSet)
2✔
213
    for tgt in sorted(all_targets, key=lambda t: t.address):
2✔
214
        if not tgt.has_field(JvmArtifactResolveField):
2✔
215
            continue
1✔
216
        artifact = ArtifactRequirement.from_jvm_artifact_target(tgt)
2✔
217
        resolve = tgt[JvmResolveField].normalized_value(jvm_subsystem)
2✔
218
        resolve_to_artifacts[resolve].add(artifact)
2✔
219

220
    tools = ExportableTool.filter_for_subclasses(union_membership, JvmToolBase)
2✔
221

222
    rule_calls: list[Coroutine[Any, Any, GenerateJvmLockfile]] = []
2✔
223
    for resolve in requested:
2✔
224
        rule_calls.append(await _plan_generate_lockfile(resolve, resolve_to_artifacts, tools))
2✔
225

226
    jvm_lockfile_requests = await concurrently(rule_calls)
2✔
227

UNCOV
228
    return UserGenerateLockfiles(jvm_lockfile_requests)
1✔
229

230

231
def rules():
12✔
232
    return (
12✔
233
        *collect_rules(),
234
        *coursier_fetch.rules(),
235
        UnionRule(GenerateLockfile, GenerateJvmLockfile),
236
        UnionRule(KnownUserResolveNamesRequest, KnownJVMUserResolveNamesRequest),
237
        UnionRule(RequestedUserResolveNames, RequestedJVMUserResolveNames),
238
    )
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