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

pantsbuild / pants / 26260209689

21 May 2026 11:59PM UTC coverage: 75.453% (-15.7%) from 91.156%
26260209689

Pull #23365

github

web-flow
Merge 5fe873b58 into 7ea655ba0
Pull Request #23365: uv.lock -> pex optimization

5 of 16 new or added lines in 1 file covered. (31.25%)

10118 existing lines in 378 files now uncovered.

54669 of 72454 relevant lines covered (75.45%)

2.31 hits per line

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

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

4
from __future__ import annotations
3✔
5

6
import logging
3✔
7
from collections import defaultdict
3✔
8
from dataclasses import dataclass
3✔
9
from typing import Any
3✔
10

11
from pants.build_graph.address import Address
3✔
12
from pants.engine.environment import EnvironmentName
3✔
13
from pants.engine.rules import collect_rules, concurrently, implicitly, rule
3✔
14
from pants.engine.unions import UnionMembership, union
3✔
15
from pants.jvm.dependency_inference.artifact_mapper import (
3✔
16
    AllJvmTypeProvidingTargets,
17
    FrozenTrieNode,
18
    SymbolNamespace,
19
    ThirdPartySymbolMapping,
20
)
21
from pants.jvm.subsystems import JvmSubsystem
3✔
22
from pants.jvm.target_types import JvmProvidesTypesField, JvmResolveField
3✔
23
from pants.util.frozendict import FrozenDict
3✔
24
from pants.util.logging import LogLevel
3✔
25
from pants.util.ordered_set import FrozenOrderedSet
3✔
26

27
logger = logging.getLogger(__name__)
3✔
28

29

30
# -----------------------------------------------------------------------------------------------
31
# First-party package mapping
32
# -----------------------------------------------------------------------------------------------
33

34

35
_ResolveName = str
3✔
36

37

38
class JvmFirstPartyPackageMappingException(Exception):
3✔
39
    pass
3✔
40

41

42
@union(in_scope_types=[EnvironmentName])
3✔
43
@dataclass(frozen=True)
3✔
44
class FirstPartyMappingRequest:
3✔
45
    """An entry point for a specific implementation of mapping JVM package names to owning targets.
46

47
    All implementations will be merged together.
48

49
    The addresses should all be file addresses, rather than BUILD addresses.
50
    """
51

52

53
class SymbolMap(FrozenDict[_ResolveName, FrozenTrieNode]):
3✔
54
    """The first party symbols provided by a single inference implementation."""
55

56

57
@rule(polymorphic=True)
3✔
58
async def create_first_party_map(
3✔
59
    req: FirstPartyMappingRequest, env_name: EnvironmentName
60
) -> SymbolMap:
61
    raise NotImplementedError()
×
62

63

64
@dataclass(frozen=True)
3✔
65
class SymbolMapping:
3✔
66
    """The merged first and third party symbols provided by all inference implementations."""
67

68
    mapping_roots: FrozenDict[_ResolveName, FrozenTrieNode]
3✔
69

70
    def addresses_for_symbol(
3✔
71
        self, symbol: str, resolve: str
72
    ) -> FrozenDict[SymbolNamespace, FrozenOrderedSet[Address]]:
73
        node = self.mapping_roots.get(resolve)
3✔
74
        # Note that it's possible to have a resolve with no associated artifacts.
75
        if not node:
3✔
76
            return FrozenDict()
×
77
        return node.addresses_for_symbol(symbol)
3✔
78

79
    def to_json_dict(self) -> dict[str, Any]:
3✔
80
        return {
×
81
            "symbol_map": {
82
                resolve: node.to_json_dict() for resolve, node in self.mapping_roots.items()
83
            }
84
        }
85

86

87
@rule(level=LogLevel.DEBUG)
3✔
88
async def merge_symbol_mappings(
3✔
89
    union_membership: UnionMembership,
90
    targets_that_provide_types: AllJvmTypeProvidingTargets,
91
    jvm: JvmSubsystem,
92
    third_party_mapping: ThirdPartySymbolMapping,
93
) -> SymbolMapping:
94
    all_firstparty_mappings = await concurrently(
3✔
95
        create_first_party_map(**implicitly({marker_cls(): FirstPartyMappingRequest}))
96
        for marker_cls in union_membership.get(FirstPartyMappingRequest)
97
    )
98
    all_mappings: list[FrozenDict[_ResolveName, FrozenTrieNode]] = [
3✔
99
        *all_firstparty_mappings,
100
        third_party_mapping,
101
    ]
102

103
    resolves = {resolve for mapping in all_mappings for resolve in mapping.keys()}
3✔
104
    mapping = SymbolMapping(
3✔
105
        FrozenDict(
106
            (
107
                resolve,
108
                FrozenTrieNode.merge(
109
                    mapping[resolve] for mapping in all_mappings if resolve in mapping
110
                ),
111
            )
112
            for resolve in resolves
113
        )
114
    )
115

116
    # `experimental_provides_types` ("`provides`") can be declared on a `java_sources` target,
117
    # so each generated `java_source` target will have that `provides` annotation. All that matters
118
    # here is that _one_ of the souce files amongst the set of sources actually provides that type.
119

120
    # Collect each address associated with a `provides` annotation and index by the provided type.
121
    provided_types: dict[tuple[str, str], set[Address]] = defaultdict(set)
3✔
122
    for tgt in targets_that_provide_types:
3✔
UNCOV
123
        resolve = tgt[JvmResolveField].normalized_value(jvm)
×
UNCOV
124
        for provided_type in tgt[JvmProvidesTypesField].value or []:
×
UNCOV
125
            provided_types[(resolve, provided_type)].add(tgt.address)
×
126

127
    # Check that at least one address declared by each `provides` value actually provides the type:
128
    for (resolve, provided_type), provided_addresses in provided_types.items():
3✔
UNCOV
129
        symbol_addresses = mapping.addresses_for_symbol(provided_type, resolve=resolve)
×
UNCOV
130
        logger.info(f"addresses for {provided_type} in {resolve}:\n  {symbol_addresses}")
×
UNCOV
131
        if not any(
×
132
            provided_addresses.intersection(ns_addresses)
133
            for ns_addresses in symbol_addresses.values()
134
        ):
UNCOV
135
            raise JvmFirstPartyPackageMappingException(
×
136
                f"The target {next(iter(provided_addresses))} declares that it provides the JVM type "
137
                f"`{provided_type}`, however, it does not appear to actually provide that type."
138
            )
139

140
    return mapping
3✔
141

142

143
def rules():
3✔
144
    return collect_rules()
3✔
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