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

pantsbuild / pants / 22285099215

22 Feb 2026 08:52PM UTC coverage: 75.854% (-17.1%) from 92.936%
22285099215

Pull #23121

github

web-flow
Merge c7299df9c into ba8359840
Pull Request #23121: fix issue with optional fields in dependency validator

28 of 29 new or added lines in 2 files covered. (96.55%)

11174 existing lines in 400 files now uncovered.

53694 of 70786 relevant lines covered (75.85%)

1.88 hits per line

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

62.79
/src/python/pants/jvm/bsp/compile.py
1
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
import time
1✔
5
from dataclasses import dataclass
1✔
6

7
from pants.bsp.context import BSPContext
1✔
8
from pants.bsp.spec.base import BuildTargetIdentifier, StatusCode, TaskId
1✔
9
from pants.bsp.spec.log import LogMessageParams, MessageType
1✔
10
from pants.bsp.spec.task import TaskProgressParams
1✔
11
from pants.bsp.util_rules.targets import BSPCompileRequest, BSPCompileResult
1✔
12
from pants.engine.addresses import Addresses
1✔
13
from pants.engine.fs import AddPrefix, MergeDigests
1✔
14
from pants.engine.internals.graph import resolve_coarsened_targets
1✔
15
from pants.engine.internals.native_engine import EMPTY_DIGEST
1✔
16
from pants.engine.internals.selectors import concurrently
1✔
17
from pants.engine.intrinsics import add_prefix, merge_digests
1✔
18
from pants.engine.rules import collect_rules, implicitly, rule
1✔
19
from pants.jvm import classpath
1✔
20
from pants.jvm.compile import (
1✔
21
    ClasspathEntryRequest,
22
    ClasspathEntryRequestFactory,
23
    CompileResult,
24
    FallibleClasspathEntry,
25
    get_fallible_classpath_entry,
26
)
27
from pants.jvm.resolve.coursier_fetch import select_coursier_resolve_for_targets
1✔
28
from pants.jvm.target_types import JvmArtifactFieldSet
1✔
29
from pants.util.strutil import path_safe
1✔
30

31

32
def jvm_classes_directory(target_id: BuildTargetIdentifier) -> str:
1✔
UNCOV
33
    return f"jvm/classes/{path_safe(target_id.uri)}"
×
34

35

36
@dataclass(frozen=True)
1✔
37
class BSPClasspathEntryRequest:
1✔
38
    """A wrapper around a `ClasspathEntryRequest` which notifies the BSP client on completion.
39

40
    TODO: Because this struct contains a `task_id`, messages will re-render in every run, even
41
    though the underlying computation does not re-run. See #15426 for an alternative.
42
    """
43

44
    request: ClasspathEntryRequest
45
    task_id: TaskId
46

47

48
@rule
1✔
49
async def notify_for_classpath_entry(
1✔
50
    request: BSPClasspathEntryRequest,
51
    context: BSPContext,
52
) -> FallibleClasspathEntry:
UNCOV
53
    entry = await get_fallible_classpath_entry(
×
54
        **implicitly({request.request: ClasspathEntryRequest})
55
    )
UNCOV
56
    context.notify_client(
×
57
        TaskProgressParams(
58
            task_id=request.task_id,
59
            event_time=int(time.time() * 1000),
60
            message=entry.message(),
61
        )
62
    )
UNCOV
63
    if entry.result == CompileResult.FAILED:
×
64
        context.notify_client(
×
65
            LogMessageParams(
66
                type_=MessageType.ERROR,
67
                message=entry.message(),
68
                task=request.task_id,
69
            )
70
        )
UNCOV
71
    return entry
×
72

73

74
async def _jvm_bsp_compile(
1✔
75
    request: BSPCompileRequest, classpath_entry_request: ClasspathEntryRequestFactory
76
) -> BSPCompileResult:
77
    """Generically handles a BSPCompileRequest (subclass).
78

79
    This is a rule helper rather than a `@rule`, because BSP backends like `java` and `scala`
80
    independently declare their `BSPCompileRequest` union members. We can't register a single shared
81
    `BSPCompileRequest` @union member for all JVM because their FieldSets are also declared via
82
    @unions, and we can't forward the implementation of a @union to another the way we might with
83
    an abstract class.
84
    """
UNCOV
85
    coarsened_targets = await resolve_coarsened_targets(
×
86
        **implicitly(Addresses([fs.address for fs in request.field_sets]))
87
    )
UNCOV
88
    resolve = await select_coursier_resolve_for_targets(coarsened_targets, **implicitly())
×
89

90
    # TODO: We include the (non-3rdparty) transitive dependencies here, because each project
91
    # currently only has a single BuildTarget. This has the effect of including `resources` targets,
92
    # which are referenced by BuildTargets (via `buildTarget/resources`), rather than necessarily
93
    # being owned by any particular BuildTarget.
94
    #
95
    # To resolve #15051, this will no longer be transitive, and so `resources` will need to be
96
    # attached-to/referenced-by nearby BuildTarget(s) instead (most likely: direct dependent(s)).
UNCOV
97
    results = await concurrently(
×
98
        notify_for_classpath_entry(
99
            BSPClasspathEntryRequest(
100
                classpath_entry_request.for_targets(component=coarsened_target, resolve=resolve),
101
                task_id=request.task_id,
102
            ),
103
            **implicitly(),
104
        )
105
        for coarsened_target in coarsened_targets.coarsened_closure()
106
        if not any(JvmArtifactFieldSet.is_applicable(t) for t in coarsened_target.members)
107
    )
108

UNCOV
109
    entries = FallibleClasspathEntry.if_all_succeeded(results)
×
UNCOV
110
    if entries is None:
×
111
        return BSPCompileResult(
×
112
            status=StatusCode.ERROR,
113
            output_digest=EMPTY_DIGEST,
114
        )
115

UNCOV
116
    loose_clsfiles = await concurrently(
×
117
        classpath.loose_classfiles(entry, **implicitly()) for entry in entries
118
    )
UNCOV
119
    merged_loose_classfiles = await merge_digests(MergeDigests(lc.digest for lc in loose_clsfiles))
×
UNCOV
120
    output_digest = await add_prefix(
×
121
        AddPrefix(merged_loose_classfiles, jvm_classes_directory(request.bsp_target.bsp_target_id)),
122
    )
123

UNCOV
124
    return BSPCompileResult(
×
125
        status=StatusCode.OK,
126
        output_digest=output_digest,
127
    )
128

129

130
def rules():
1✔
131
    return [
1✔
132
        *collect_rules(),
133
        *classpath.rules(),
134
    ]
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