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

pantsbuild / pants / 19015773527

02 Nov 2025 05:33PM UTC coverage: 17.872% (-62.4%) from 80.3%
19015773527

Pull #22816

github

web-flow
Merge a12d75757 into 6c024e162
Pull Request #22816: Update Pants internal Python to 3.14

4 of 5 new or added lines in 3 files covered. (80.0%)

28452 existing lines in 683 files now uncovered.

9831 of 55007 relevant lines covered (17.87%)

0.18 hits per line

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

0.0
/src/python/pants/backend/java/dependency_inference/java_parser.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

UNCOV
4
from __future__ import annotations
×
5

UNCOV
6
import importlib.resources
×
UNCOV
7
import json
×
UNCOV
8
import logging
×
UNCOV
9
import os.path
×
UNCOV
10
from dataclasses import dataclass
×
11

UNCOV
12
from pants.backend.java.dependency_inference.types import JavaSourceDependencyAnalysis
×
UNCOV
13
from pants.core.goals.resolves import ExportableTool
×
UNCOV
14
from pants.core.util_rules.source_files import SourceFiles
×
UNCOV
15
from pants.engine.fs import AddPrefix, CreateDigest, Digest, Directory, FileContent
×
UNCOV
16
from pants.engine.internals.native_engine import MergeDigests, RemovePrefix
×
UNCOV
17
from pants.engine.intrinsics import (
×
18
    add_prefix,
19
    create_digest,
20
    execute_process,
21
    get_digest_contents,
22
    merge_digests,
23
    remove_prefix,
24
)
UNCOV
25
from pants.engine.process import (
×
26
    FallibleProcessResult,
27
    ProductDescription,
28
    execute_process_or_raise,
29
    fallible_to_exec_result_or_raise,
30
)
UNCOV
31
from pants.engine.rules import collect_rules, concurrently, implicitly, rule
×
UNCOV
32
from pants.engine.unions import UnionRule
×
UNCOV
33
from pants.jvm.jdk_rules import InternalJdk, JvmProcess
×
UNCOV
34
from pants.jvm.resolve.coursier_fetch import ToolClasspathRequest, materialize_classpath_for_tool
×
UNCOV
35
from pants.jvm.resolve.jvm_tool import GenerateJvmLockfileFromTool, JvmToolBase
×
UNCOV
36
from pants.util.logging import LogLevel
×
37

UNCOV
38
logger = logging.getLogger(__name__)
×
39

40

UNCOV
41
_LAUNCHER_BASENAME = "PantsJavaParserLauncher.java"
×
42

43

UNCOV
44
class JavaParser(JvmToolBase):
×
UNCOV
45
    options_scope = "java-parser"
×
UNCOV
46
    help = "Internal tool for parsing JVM sources to identify dependencies"
×
47

UNCOV
48
    default_artifacts = (
×
49
        "com.fasterxml.jackson.core:jackson-databind:2.12.4",
50
        "com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.12.4",
51
        "com.github.javaparser:javaparser-symbol-solver-core:3.25.5",
52
    )
UNCOV
53
    default_lockfile_resource = (
×
54
        "pants.backend.java.dependency_inference",
55
        "java_parser.lock",
56
    )
57

58

UNCOV
59
@dataclass(frozen=True)
×
UNCOV
60
class JavaSourceDependencyAnalysisRequest:
×
UNCOV
61
    source_files: SourceFiles
×
62

63

UNCOV
64
@dataclass(frozen=True)
×
UNCOV
65
class FallibleJavaSourceDependencyAnalysisResult:
×
UNCOV
66
    process_result: FallibleProcessResult
×
67

68

UNCOV
69
@dataclass(frozen=True)
×
UNCOV
70
class JavaParserCompiledClassfiles:
×
UNCOV
71
    digest: Digest
×
72

73

UNCOV
74
@rule(level=LogLevel.DEBUG)
×
UNCOV
75
async def resolve_fallible_result_to_analysis(
×
76
    fallible_result: FallibleJavaSourceDependencyAnalysisResult,
77
) -> JavaSourceDependencyAnalysis:
78
    desc = ProductDescription("Java source dependency analysis failed.")
×
79
    result = await fallible_to_exec_result_or_raise(
×
80
        **implicitly(
81
            {fallible_result.process_result: FallibleProcessResult, desc: ProductDescription}
82
        )
83
    )
84
    analysis_contents = await get_digest_contents(result.output_digest)
×
85
    analysis = json.loads(analysis_contents[0].content)
×
86
    return JavaSourceDependencyAnalysis.from_json_dict(analysis)
×
87

88

UNCOV
89
@rule(level=LogLevel.DEBUG)
×
UNCOV
90
async def make_analysis_request_from_source_files(
×
91
    source_files: SourceFiles,
92
) -> JavaSourceDependencyAnalysisRequest:
93
    return JavaSourceDependencyAnalysisRequest(source_files=source_files)
×
94

95

UNCOV
96
@rule(level=LogLevel.DEBUG)
×
UNCOV
97
async def analyze_java_source_dependencies(
×
98
    processor_classfiles: JavaParserCompiledClassfiles,
99
    jdk: InternalJdk,
100
    tool: JavaParser,
101
    request: JavaSourceDependencyAnalysisRequest,
102
) -> FallibleJavaSourceDependencyAnalysisResult:
103
    source_files = request.source_files
×
104
    if len(source_files.files) > 1:
×
105
        raise ValueError(
×
106
            f"parse_java_package expects sources with exactly 1 source file, but found {len(source_files.files)}."
107
        )
108
    elif len(source_files.files) == 0:
×
109
        raise ValueError(
×
110
            "parse_java_package expects sources with exactly 1 source file, but found none."
111
        )
112
    source_prefix = "__source_to_analyze"
×
113
    source_path = os.path.join(source_prefix, source_files.files[0])
×
114
    processorcp_relpath = "__processorcp"
×
115
    toolcp_relpath = "__toolcp"
×
116

117
    tool_classpath, prefixed_source_files_digest = await concurrently(
×
118
        materialize_classpath_for_tool(
119
            ToolClasspathRequest(lockfile=(GenerateJvmLockfileFromTool.create(tool)))
120
        ),
121
        add_prefix(AddPrefix(source_files.snapshot.digest, source_prefix)),
122
    )
123

124
    extra_immutable_input_digests = {
×
125
        toolcp_relpath: tool_classpath.digest,
126
        processorcp_relpath: processor_classfiles.digest,
127
    }
128

129
    analysis_output_path = "__source_analysis.json"
×
130

131
    process_result = await execute_process(
×
132
        **implicitly(
133
            JvmProcess(
134
                jdk=jdk,
135
                classpath_entries=[
136
                    *tool_classpath.classpath_entries(toolcp_relpath),
137
                    processorcp_relpath,
138
                ],
139
                argv=[
140
                    "org.pantsbuild.javaparser.PantsJavaParserLauncher",
141
                    analysis_output_path,
142
                    source_path,
143
                ],
144
                input_digest=prefixed_source_files_digest,
145
                extra_immutable_input_digests=extra_immutable_input_digests,
146
                output_files=(analysis_output_path,),
147
                extra_nailgun_keys=extra_immutable_input_digests,
148
                description=f"Analyzing {source_files.files[0]}",
149
                level=LogLevel.DEBUG,
150
            )
151
        )
152
    )
153

154
    return FallibleJavaSourceDependencyAnalysisResult(process_result=process_result)
×
155

156

UNCOV
157
def _load_javaparser_launcher_source() -> bytes:
×
158
    parent_module = ".".join(__name__.split(".")[:-1])
×
159
    return importlib.resources.files(parent_module).joinpath(_LAUNCHER_BASENAME).read_bytes()
×
160

161

162
# TODO(13879): Consolidate compilation of wrapper binaries to common rules.
UNCOV
163
@rule
×
UNCOV
164
async def build_processors(jdk: InternalJdk, tool: JavaParser) -> JavaParserCompiledClassfiles:
×
165
    dest_dir = "classfiles"
×
166
    materialized_classpath, source_digest = await concurrently(
×
167
        materialize_classpath_for_tool(
168
            ToolClasspathRequest(
169
                prefix="__toolcp", lockfile=GenerateJvmLockfileFromTool.create(tool)
170
            )
171
        ),
172
        create_digest(
173
            CreateDigest(
174
                [
175
                    FileContent(
176
                        path=_LAUNCHER_BASENAME,
177
                        content=_load_javaparser_launcher_source(),
178
                    ),
179
                    Directory(dest_dir),
180
                ]
181
            )
182
        ),
183
    )
184

185
    merged_digest = await merge_digests(
×
186
        MergeDigests(
187
            (
188
                materialized_classpath.digest,
189
                source_digest,
190
            )
191
        )
192
    )
193

194
    process_result = await execute_process_or_raise(
×
195
        **implicitly(
196
            JvmProcess(
197
                jdk=jdk,
198
                classpath_entries=[f"{jdk.java_home}/lib/tools.jar"],
199
                argv=[
200
                    "com.sun.tools.javac.Main",
201
                    "-cp",
202
                    ":".join(materialized_classpath.classpath_entries()),
203
                    "-d",
204
                    dest_dir,
205
                    _LAUNCHER_BASENAME,
206
                ],
207
                input_digest=merged_digest,
208
                output_directories=(dest_dir,),
209
                description=f"Compile {_LAUNCHER_BASENAME} import processors with javac",
210
                level=LogLevel.DEBUG,
211
                # NB: We do not use nailgun for this process, since it is launched exactly once.
212
                use_nailgun=False,
213
            )
214
        )
215
    )
216
    stripped_classfiles_digest = await remove_prefix(
×
217
        RemovePrefix(process_result.output_digest, dest_dir)
218
    )
219
    return JavaParserCompiledClassfiles(digest=stripped_classfiles_digest)
×
220

221

UNCOV
222
def rules():
×
UNCOV
223
    return (
×
224
        *collect_rules(),
225
        UnionRule(ExportableTool, JavaParser),
226
    )
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

© 2025 Coveralls, Inc