• 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/rules.py
1
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
UNCOV
3
from __future__ import annotations
×
4

UNCOV
5
from collections import defaultdict
×
UNCOV
6
from dataclasses import dataclass
×
7

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

38

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

UNCOV
43
    source: JavaSourceField
×
44

45

UNCOV
46
class InferJavaSourceDependencies(InferDependenciesRequest):
×
UNCOV
47
    infer_from = JavaSourceDependenciesInferenceFieldSet
×
48

49

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

55

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

60

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

71
    address = request.source.address
×
72

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

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

94
        # 13545: `analysis.consumed_types` may be unqualified (package-local or imported) or qualified
95
        # (prefixed by package name). Heuristic for now is that if there's a `.` in the type name, it's
96
        # probably fully qualified. This is probably fine for now.
97
        maybe_qualify_types = (
×
98
            f"{package}.{consumed_type}" if package and "." not in consumed_type else consumed_type
99
            for consumed_type in analysis.consumed_types
100
        )
101

102
        types.update(maybe_qualify_types)
×
103

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

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

120
    resolve = tgt[JvmResolveField].normalized_value(jvm)
×
121

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

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

143
    # Files do not export themselves. Don't be silly.
144
    if address in exports:
×
145
        exports.remove(address)
×
146

147
    return JavaInferredDependencies(FrozenOrderedSet(dependencies), FrozenOrderedSet(exports))
×
148

149

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

159

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

166

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