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

pantsbuild / pants / 22523112068

28 Feb 2026 03:01PM UTC coverage: 90.325% (-2.6%) from 92.93%
22523112068

push

github

web-flow
Prepare 2.32.0.dev3 (#23148)

82731 of 91593 relevant lines covered (90.32%)

3.28 hits per line

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

96.51
/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
7✔
4

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

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

38

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

43
    source: JavaSourceField
7✔
44

45

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

49

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

55

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

60

61
@rule(desc="Inferring Java dependencies and exports by source analysis")
7✔
62
async def infer_java_dependencies_and_exports_via_source_analysis(
7✔
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:
7✔
69
        return JavaInferredDependencies(FrozenOrderedSet([]), FrozenOrderedSet([]))
×
70

71
    address = request.source.address
7✔
72

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

79
    explicitly_provided_deps, analysis = await concurrently(
7✔
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()
7✔
89
    if java_infer_subsystem.imports:
7✔
90
        types.update(dependency_name(imp) for imp in analysis.imports)
7✔
91
    if java_infer_subsystem.consumed_types:
7✔
92
        package = analysis.declared_package
7✔
93

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

102
                # Might be an inner class in the same package as the current class
103
                first_part = consumed_type.split(".")[0]
2✔
104
                types.add(f"{package}.{first_part}" if package else first_part)
2✔
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)
7✔
109
    for typ in types:
7✔
110
        unqualified = typ.rpartition(".")[2]  # `"org.foo.Java"` -> `("org.foo", ".", "Java")`
7✔
111
        consumed_type_mapping[unqualified].add(typ)
7✔
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 = {
7✔
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)
7✔
121

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

124
    dependencies: OrderedSet[Address] = OrderedSet()
7✔
125
    exports: OrderedSet[Address] = OrderedSet()
7✔
126
    for typ in types:
7✔
127
        for matches in symbol_mapping.addresses_for_symbol(typ, resolve).values():
7✔
128
            explicitly_provided_deps.maybe_warn_of_ambiguous_dependency_inference(
6✔
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)
6✔
135

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

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

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

151

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

161

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

168

169
def rules():
7✔
170
    return [
7✔
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