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

pantsbuild / pants / 23074067894

13 Mar 2026 11:06PM UTC coverage: 64.165% (-28.8%) from 92.932%
23074067894

Pull #23171

github

web-flow
Merge 17d8ea7d8 into f07276df6
Pull Request #23171: Debug reapi test cache misses

42163 of 65710 relevant lines covered (64.17%)

0.99 hits per line

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

81.4
/src/python/pants/backend/java/dependency_inference/rules.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
from __future__ import annotations
1✔
4

5
from collections import defaultdict
1✔
6
from dataclasses import dataclass
1✔
7

8
from pants.backend.java.dependency_inference import symbol_mapper
1✔
9
from pants.backend.java.dependency_inference.java_parser import (
1✔
10
    JavaSourceDependencyAnalysisRequest,
11
    resolve_fallible_result_to_analysis,
12
)
13
from pants.backend.java.dependency_inference.java_parser import rules as java_parser_rules
1✔
14
from pants.backend.java.dependency_inference.types import JavaImport
1✔
15
from pants.backend.java.subsystems.java_infer import JavaInferSubsystem
1✔
16
from pants.backend.java.target_types import JavaSourceField
1✔
17
from pants.core.util_rules.source_files import SourceFilesRequest, determine_source_files
1✔
18
from pants.core.util_rules.source_files import rules as source_files_rules
1✔
19
from pants.engine.addresses import Address
1✔
20
from pants.engine.internals.graph import determine_explicitly_provided_dependencies, resolve_target
1✔
21
from pants.engine.rules import collect_rules, concurrently, implicitly, rule
1✔
22
from pants.engine.target import (
1✔
23
    Dependencies,
24
    DependenciesRequest,
25
    FieldSet,
26
    InferDependenciesRequest,
27
    InferredDependencies,
28
    SourcesField,
29
    WrappedTargetRequest,
30
)
31
from pants.engine.unions import UnionRule
1✔
32
from pants.jvm.dependency_inference import artifact_mapper
1✔
33
from pants.jvm.dependency_inference.symbol_mapper import SymbolMapping
1✔
34
from pants.jvm.subsystems import JvmSubsystem
1✔
35
from pants.jvm.target_types import JvmResolveField
1✔
36
from pants.util.ordered_set import FrozenOrderedSet, OrderedSet
1✔
37

38

39
@dataclass(frozen=True)
1✔
40
class JavaSourceDependenciesInferenceFieldSet(FieldSet):
1✔
41
    required_fields = (JavaSourceField,)
1✔
42

43
    source: JavaSourceField
1✔
44

45

46
class InferJavaSourceDependencies(InferDependenciesRequest):
1✔
47
    infer_from = JavaSourceDependenciesInferenceFieldSet
1✔
48

49

50
@dataclass(frozen=True)
1✔
51
class JavaInferredDependencies:
1✔
52
    dependencies: FrozenOrderedSet[Address]
1✔
53
    exports: FrozenOrderedSet[Address]
1✔
54

55

56
@dataclass(frozen=True)
1✔
57
class JavaInferredDependenciesAndExportsRequest:
1✔
58
    source: SourcesField
1✔
59

60

61
@rule(desc="Inferring Java dependencies and exports by source analysis")
1✔
62
async def infer_java_dependencies_and_exports_via_source_analysis(
1✔
63
    request: JavaInferredDependenciesAndExportsRequest,
64
    java_infer_subsystem: JavaInferSubsystem,
65
    jvm: JvmSubsystem,
66
    symbol_mapping: SymbolMapping,
67
) -> JavaInferredDependencies:
68
    if not java_infer_subsystem.imports and not java_infer_subsystem.consumed_types:
1✔
69
        return JavaInferredDependencies(FrozenOrderedSet([]), FrozenOrderedSet([]))
×
70

71
    address = request.source.address
1✔
72

73
    wrapped_tgt = await resolve_target(
1✔
74
        WrappedTargetRequest(address, description_of_origin="<infallible>"), **implicitly()
75
    )
76
    tgt = wrapped_tgt.target
1✔
77
    source_files = await determine_source_files(SourceFilesRequest([tgt[JavaSourceField]]))
1✔
78

79
    explicitly_provided_deps, analysis = await concurrently(
1✔
80
        determine_explicitly_provided_dependencies(
81
            **implicitly(DependenciesRequest(tgt[Dependencies]))
82
        ),
83
        resolve_fallible_result_to_analysis(
84
            **implicitly(JavaSourceDependencyAnalysisRequest(source_files=source_files))
85
        ),
86
    )
87

88
    types: OrderedSet[str] = OrderedSet()
1✔
89
    if java_infer_subsystem.imports:
1✔
90
        types.update(dependency_name(imp) for imp in analysis.imports)
1✔
91
    if java_infer_subsystem.consumed_types:
1✔
92
        package = analysis.declared_package
1✔
93

94
        for consumed_type in analysis.consumed_types:
1✔
95
            if "." not in consumed_type:
1✔
96
                # Simple unqualified type - add package prefix
97
                types.add(f"{package}.{consumed_type}" if package else consumed_type)
1✔
98
            else:
99
                # Has dots and might already be fully qualified
100
                types.add(consumed_type)
×
101

102
                # Might be an inner class in the same package as the current class
103
                first_part = consumed_type.split(".")[0]
×
104
                types.add(f"{package}.{first_part}" if package else first_part)
×
105

106
    # Resolve the export types into (probable) types:
107
    # First produce a map of known consumed unqualified types to possible qualified names
108
    consumed_type_mapping: dict[str, set[str]] = defaultdict(set)
1✔
109
    for typ in types:
1✔
110
        unqualified = typ.rpartition(".")[2]  # `"org.foo.Java"` -> `("org.foo", ".", "Java")`
1✔
111
        consumed_type_mapping[unqualified].add(typ)
1✔
112

113
    # Now take the list of unqualified export types and convert them to possible
114
    # qualified names based on the guesses we made for consumed types
115
    export_types = {
1✔
116
        i for typ in analysis.export_types for i in consumed_type_mapping.get(typ, set())
117
    }
118
    # Finally, if there's a `.` in the name, it's probably fully qualified,
119
    # so just add it unaltered
120
    export_types.update(typ for typ in analysis.export_types if "." in typ)
1✔
121

122
    resolve = tgt[JvmResolveField].normalized_value(jvm)
1✔
123

124
    dependencies: OrderedSet[Address] = OrderedSet()
1✔
125
    exports: OrderedSet[Address] = OrderedSet()
1✔
126
    for typ in types:
1✔
127
        for matches in symbol_mapping.addresses_for_symbol(typ, resolve).values():
1✔
128
            explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
×
129
                matches,
130
                address,
131
                import_reference="type",
132
                context=f"The target {address} imports `{typ}`",
133
            )
134
            maybe_disambiguated = explicitly_provided_deps.disambiguated(matches)
×
135

136
            if maybe_disambiguated:
×
137
                dependencies.add(maybe_disambiguated)
×
138
                if typ in export_types:
×
139
                    exports.add(maybe_disambiguated)
×
140
            else:
141
                # Exports from explicitly provided dependencies:
142
                explicitly_provided_exports = set(matches) & set(explicitly_provided_deps.includes)
×
143
                exports.update(explicitly_provided_exports)
×
144

145
    # Files do not export themselves. Don't be silly.
146
    if address in exports:
1✔
147
        exports.remove(address)
×
148

149
    return JavaInferredDependencies(FrozenOrderedSet(dependencies), FrozenOrderedSet(exports))
1✔
150

151

152
@rule(desc="Inferring Java dependencies by source analysis")
1✔
153
async def infer_java_dependencies_via_source_analysis(
1✔
154
    request: InferJavaSourceDependencies,
155
) -> InferredDependencies:
156
    jids = await infer_java_dependencies_and_exports_via_source_analysis(
1✔
157
        JavaInferredDependenciesAndExportsRequest(request.field_set.source), **implicitly()
158
    )
159
    return InferredDependencies(jids.dependencies)
1✔
160

161

162
def dependency_name(imp: JavaImport):
1✔
163
    if imp.is_static and not imp.is_asterisk:
×
164
        return imp.name.rsplit(".", maxsplit=1)[0]
×
165
    else:
166
        return imp.name
×
167

168

169
def rules():
1✔
170
    return [
1✔
171
        *collect_rules(),
172
        *artifact_mapper.rules(),
173
        *java_parser_rules(),
174
        *symbol_mapper.rules(),
175
        *source_files_rules(),
176
        UnionRule(InferDependenciesRequest, InferJavaSourceDependencies),
177
    ]
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