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

pantsbuild / pants / 24055979590

06 Apr 2026 11:17PM UTC coverage: 52.37% (-40.5%) from 92.908%
24055979590

Pull #23225

github

web-flow
Merge 67474653c into 542ca048d
Pull Request #23225: Add --test-show-all-batch-targets to expose all targets in batched pytest

6 of 17 new or added lines in 2 files covered. (35.29%)

23030 existing lines in 605 files now uncovered.

31643 of 60422 relevant lines covered (52.37%)

1.05 hits per line

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

60.61
/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
2✔
5

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

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

50

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

55

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

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

64
    artifacts: ArtifactRequirements
2✔
65
    resolve_name: str
2✔
66

67

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

73

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

80

81
@rule
2✔
82
async def wrap_jvm_lockfile_request(request: GenerateJvmLockfile) -> WrappedGenerateLockfile:
2✔
83
    return WrappedGenerateLockfile(request)
×
84

85

86
@rule(desc="Generate JVM lockfile", level=LogLevel.DEBUG)
2✔
87
async def generate_jvm_lockfile(
2✔
88
    request: GenerateJvmLockfile,
89
    generate_lockfiles_subsystem: GenerateLockfilesSubsystem,
90
) -> GenerateLockfileResult:
UNCOV
91
    resolved_lockfile = await coursier_resolve_lockfile(request.artifacts)
×
UNCOV
92
    regenerate_command = (
×
93
        generate_lockfiles_subsystem.custom_command or f"{bin_name()} generate-lockfiles"
94
    )
95

UNCOV
96
    resolved_lockfile_contents = resolved_lockfile.to_serialized()
×
UNCOV
97
    metadata = JVMLockfileMetadata.new(request.artifacts)
×
UNCOV
98
    resolved_lockfile_contents = metadata.add_header_to_lockfile(
×
99
        resolved_lockfile_contents,
100
        regenerate_command=regenerate_command,
101
        delimeter="#",
102
    )
103

UNCOV
104
    lockfile_digest = await create_digest(
×
105
        CreateDigest([FileContent(request.lockfile_dest, resolved_lockfile_contents)])
106
    )
UNCOV
107
    return GenerateLockfileResult(lockfile_digest, request.resolve_name, request.lockfile_dest)
×
108

109

110
class RequestedJVMUserResolveNames(RequestedUserResolveNames):
2✔
111
    pass
2✔
112

113

114
class KnownJVMUserResolveNamesRequest(KnownUserResolveNamesRequest):
2✔
115
    pass
2✔
116

117

118
@rule
2✔
119
async def determine_jvm_user_resolves(
2✔
120
    _: KnownJVMUserResolveNamesRequest,
121
    jvm_subsystem: JvmSubsystem,
122
    union_membership: UnionMembership,
123
) -> KnownUserResolveNames:
124
    jvm_tool_resolves = ExportableTool.filter_for_subclasses(union_membership, JvmToolBase)
×
125
    names = (*jvm_subsystem.resolves.keys(), *jvm_tool_resolves.keys())
×
126
    return KnownUserResolveNames(
×
127
        names=names,
128
        option_name=f"[{jvm_subsystem.options_scope}].resolves",
129
        requested_resolve_names_cls=RequestedJVMUserResolveNames,
130
    )
131

132

133
@dataclass(frozen=True)
2✔
134
class _ValidateJvmArtifactsRequest:
2✔
135
    artifacts: ArtifactRequirements
2✔
136
    resolve_name: str
2✔
137

138

139
@rule
2✔
140
async def validate_jvm_artifacts_for_resolve(
2✔
141
    request: _ValidateJvmArtifactsRequest,
142
    union_membership: UnionMembership,
143
    jvm_subsystem: JvmSubsystem,
144
) -> GenerateJvmLockfile:
UNCOV
145
    impls = union_membership.get(ValidateJvmArtifactsForResolveRequest)
×
UNCOV
146
    for impl in impls:
×
UNCOV
147
        validate_request = impl(artifacts=request.artifacts, resolve_name=request.resolve_name)
×
UNCOV
148
        _ = await _validate_jvm_artifacts_for_resolve(
×
149
            **implicitly({validate_request: ValidateJvmArtifactsForResolveRequest})
150
        )
151

UNCOV
152
    return GenerateJvmLockfile(
×
153
        artifacts=request.artifacts,
154
        resolve_name=request.resolve_name,
155
        lockfile_dest=jvm_subsystem.resolves[request.resolve_name],
156
        diff=False,
157
    )
158

159

160
@rule
2✔
161
async def setup_lockfile_request_from_tool(
2✔
162
    request: GenerateJvmLockfileFromTool,
163
) -> GenerateJvmLockfile:
UNCOV
164
    artifacts = await gather_coordinates_for_jvm_lockfile(
×
165
        GatherJvmCoordinatesRequest(request.artifact_inputs, request.artifact_option_name)
166
    )
UNCOV
167
    return GenerateJvmLockfile(
×
168
        artifacts=artifacts,
169
        resolve_name=request.resolve_name,
170
        lockfile_dest=(
171
            request.lockfile if request.lockfile != DEFAULT_TOOL_LOCKFILE else DEFAULT_TOOL_LOCKFILE
172
        ),
173
        diff=False,
174
    )
175

176

177
async def _plan_generate_lockfile(
2✔
178
    resolve, resolve_to_artifacts, tools
179
) -> Coroutine[Any, Any, GenerateJvmLockfile]:
180
    """Generate a JVM lockfile request for each requested resolve.
181

182
    This step also allows other backends to validate the proposed set of artifact requirements for
183
    each resolve.
184
    """
UNCOV
185
    if resolve in resolve_to_artifacts:
×
UNCOV
186
        return validate_jvm_artifacts_for_resolve(
×
187
            _ValidateJvmArtifactsRequest(
188
                artifacts=ArtifactRequirements(resolve_to_artifacts[resolve]),
189
                resolve_name=resolve,
190
            ),
191
            **implicitly(),
192
        )
UNCOV
193
    elif resolve in tools:
×
UNCOV
194
        tool_cls: type[JvmToolBase] = tools[resolve]
×
UNCOV
195
        tool = await _construct_subsystem(tool_cls)
×
196

UNCOV
197
        return setup_lockfile_request_from_tool(
×
198
            GenerateJvmLockfileFromTool.create(tool),
199
        )
200

201
    else:
UNCOV
202
        return validate_jvm_artifacts_for_resolve(
×
203
            _ValidateJvmArtifactsRequest(
204
                artifacts=ArtifactRequirements(()),
205
                resolve_name=resolve,
206
            ),
207
            **implicitly(),
208
        )
209

210

211
@rule
2✔
212
async def setup_user_lockfile_requests(
2✔
213
    requested: RequestedJVMUserResolveNames,
214
    all_targets: AllTargets,
215
    jvm_subsystem: JvmSubsystem,
216
    union_membership: UnionMembership,
217
) -> UserGenerateLockfiles:
UNCOV
218
    resolve_to_artifacts: Mapping[str, OrderedSet[ArtifactRequirement]] = defaultdict(OrderedSet)
×
UNCOV
219
    for tgt in sorted(all_targets, key=lambda t: t.address):
×
UNCOV
220
        if not tgt.has_field(JvmArtifactResolveField):
×
UNCOV
221
            continue
×
UNCOV
222
        artifact = ArtifactRequirement.from_jvm_artifact_target(tgt)
×
UNCOV
223
        resolve = tgt[JvmResolveField].normalized_value(jvm_subsystem)
×
UNCOV
224
        resolve_to_artifacts[resolve].add(artifact)
×
225

UNCOV
226
    tools = ExportableTool.filter_for_subclasses(union_membership, JvmToolBase)
×
227

UNCOV
228
    rule_calls: list[Coroutine[Any, Any, GenerateJvmLockfile]] = []
×
UNCOV
229
    for resolve in requested:
×
UNCOV
230
        rule_calls.append(await _plan_generate_lockfile(resolve, resolve_to_artifacts, tools))
×
231

UNCOV
232
    jvm_lockfile_requests = await concurrently(rule_calls)
×
233

UNCOV
234
    return UserGenerateLockfiles(jvm_lockfile_requests)
×
235

236

237
def rules():
2✔
238
    return (
2✔
239
        *collect_rules(),
240
        *coursier_fetch.rules(),
241
        UnionRule(GenerateLockfile, GenerateJvmLockfile),
242
        UnionRule(KnownUserResolveNamesRequest, KnownJVMUserResolveNamesRequest),
243
        UnionRule(RequestedUserResolveNames, RequestedJVMUserResolveNames),
244
    )
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