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

pantsbuild / pants / 21552830208

31 Jan 2026 11:40PM UTC coverage: 80.277% (-0.05%) from 80.324%
21552830208

Pull #23062

github

web-flow
Merge 808a9786c into 2c4dcf9cf
Pull Request #23062: Remove support for Get

18 of 25 new or added lines in 4 files covered. (72.0%)

17119 existing lines in 541 files now uncovered.

78278 of 97510 relevant lines covered (80.28%)

3.36 hits per line

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

50.63
/src/python/pants/backend/python/goals/run_python_requirement.py
1
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

UNCOV
4
from __future__ import annotations
4✔
5

UNCOV
6
import logging
4✔
UNCOV
7
import os
4✔
UNCOV
8
from collections import defaultdict
4✔
UNCOV
9
from dataclasses import dataclass
4✔
UNCOV
10
from typing import TypeAlias
4✔
11

UNCOV
12
from packaging.utils import canonicalize_name as canonicalize_project_name
4✔
13

UNCOV
14
from pants.backend.python.dependency_inference.module_mapper import (
4✔
15
    ResolveName,
16
    ThirdPartyPythonModuleMapping,
17
)
UNCOV
18
from pants.backend.python.subsystems.setup import PythonSetup
4✔
UNCOV
19
from pants.backend.python.target_types import (
4✔
20
    EntryPoint,
21
    PythonRequirementDependenciesField,
22
    PythonRequirementEntryPointField,
23
    PythonRequirementModulesField,
24
    PythonRequirementResolveField,
25
    PythonRequirementsField,
26
    PythonRequirementTypeStubModulesField,
27
    ResolvePexEntryPointRequest,
28
)
UNCOV
29
from pants.backend.python.target_types_rules import resolve_pex_entry_point
4✔
UNCOV
30
from pants.backend.python.util_rules.pex import VenvPexRequest, create_venv_pex
4✔
UNCOV
31
from pants.backend.python.util_rules.pex_environment import PexEnvironment
4✔
UNCOV
32
from pants.backend.python.util_rules.pex_from_targets import (
4✔
33
    PexFromTargetsRequest,
34
    create_pex_from_targets,
35
)
UNCOV
36
from pants.build_graph.address import Address
4✔
UNCOV
37
from pants.core.goals.run import RunFieldSet, RunInSandboxBehavior, RunRequest
4✔
UNCOV
38
from pants.engine.rules import collect_rules, implicitly, rule
4✔
UNCOV
39
from pants.util.frozendict import FrozenDict
4✔
UNCOV
40
from pants.util.logging import LogLevel
4✔
UNCOV
41
from pants.util.memo import memoized
4✔
42

UNCOV
43
logger = logging.getLogger(__name__)
4✔
44

UNCOV
45
InvertedModuleMapping: TypeAlias = FrozenDict[Address, tuple[str, ...]]
4✔
46

47

UNCOV
48
def _in_chroot(relpath: str) -> str:
4✔
49
    return os.path.join("{chroot}", relpath)
×
50

51

UNCOV
52
@dataclass(frozen=True)
4✔
UNCOV
53
class PythonRequirementFieldSet(RunFieldSet):
4✔
UNCOV
54
    supports_debug_adapter = False
4✔
UNCOV
55
    run_in_sandbox_behavior = RunInSandboxBehavior.RUN_REQUEST_HERMETIC
4✔
56

UNCOV
57
    required_fields = (
4✔
58
        PythonRequirementsField,
59
        PythonRequirementDependenciesField,
60
        PythonRequirementModulesField,
61
        PythonRequirementTypeStubModulesField,
62
        PythonRequirementResolveField,
63
        PythonRequirementEntryPointField,
64
    )
65

UNCOV
66
    requirements: PythonRequirementsField
4✔
UNCOV
67
    dependencies: PythonRequirementDependenciesField
4✔
UNCOV
68
    modules: PythonRequirementModulesField
4✔
UNCOV
69
    resolve: PythonRequirementResolveField
4✔
UNCOV
70
    entry_point: PythonRequirementEntryPointField
4✔
71

72

UNCOV
73
@memoized
4✔
UNCOV
74
def _invert_module_mapping(
4✔
75
    resolve: ResolveName, module_mapping: ThirdPartyPythonModuleMapping
76
) -> InvertedModuleMapping:
77
    """Provide an inverted module mapping that can specify the set of modules known to be fulfilled
78
    by a given target address."""
79
    d: dict[Address, list[str]] = defaultdict(list)
×
80
    modules_to_providers = module_mapping.resolves_to_modules_to_providers.get(resolve)
×
81
    if not modules_to_providers:
×
82
        return FrozenDict()
×
83

84
    for module_name, providers in modules_to_providers.items():
×
85
        for provider in providers:
×
86
            d[provider.addr].append(module_name)
×
87

88
    return FrozenDict((address, tuple(modules)) for address, modules in d.items())
×
89

90

UNCOV
91
async def _resolve_entry_point(
4✔
92
    module_mapping: InvertedModuleMapping, field_set: PythonRequirementFieldSet
93
) -> EntryPoint:
94
    modules = field_set.modules.value
×
95
    reqs = field_set.requirements.value
×
96
    entry_point_raw = field_set.entry_point.value
×
97

98
    if entry_point_raw:
×
99
        resolved_entry_point = await resolve_pex_entry_point(
×
100
            ResolvePexEntryPointRequest(field_set.entry_point)
101
        )
102
        entry_point = resolved_entry_point.val
×
103

104
        if not entry_point:
×
105
            raise Exception("The provided `entry_point` was not valid.")
×
106

107
        return entry_point
×
108

109
    entry_point_module: str
110
    if modules and len(modules) == 1:
×
111
        # Unambiguous module specified in the `BUILD` file
112
        entry_point_module = modules[0]
×
113
    elif len(module_mapping.get(field_set.address, ())) == 1:
×
114
        # Check the third-party module mapping
115
        entry_point_module = module_mapping[field_set.address][0]
×
116
    elif len(reqs) == 1:
×
117
        # Use the canonicalized project name for a single-requirement target
118
        entry_point_module = canonicalize_project_name(reqs[0].name)
×
119
    else:
120
        raise Exception(
×
121
            "Requirement must provide a single module, specify a single requirement, or specify "
122
            "an `entry_point`"
123
        )
124

125
    return EntryPoint(entry_point_module)
×
126

127

UNCOV
128
@rule(level=LogLevel.DEBUG)
4✔
UNCOV
129
async def create_python_requirement_run_request(
4✔
130
    field_set: PythonRequirementFieldSet,
131
    pex_env: PexEnvironment,
132
    python_setup: PythonSetup,
133
    module_mapping: ThirdPartyPythonModuleMapping,
134
) -> RunRequest:
135
    addresses = [field_set.address]
×
136

137
    resolve = field_set.resolve.value
×
138
    if not resolve:
×
139
        resolve = python_setup.default_resolve
×
140

141
    modules_for_address = _invert_module_mapping(resolve, module_mapping)
×
142
    entry_point = await _resolve_entry_point(modules_for_address, field_set)
×
143
    filename = entry_point.spec.replace(".", "__").replace(":", "___")
×
144

145
    pex_request = await create_pex_from_targets(
×
146
        PexFromTargetsRequest(
147
            addresses,
148
            output_filename=f"{filename}.pex",
149
            internal_only=True,
150
            include_source_files=False,
151
            include_local_dists=True,
152
            main=entry_point,
153
            additional_args=("--no-strip-pex-env",),
154
        ),
155
        **implicitly(),
156
    )
157

158
    complete_pex_environment = pex_env.in_sandbox(working_directory=None)
×
159
    venv_pex = await create_venv_pex(
×
160
        VenvPexRequest(pex_request, complete_pex_environment), **implicitly()
161
    )
162
    input_digest = venv_pex.digest
×
163

164
    extra_env = {
×
165
        **complete_pex_environment.environment_dict(python_configured=venv_pex.python is not None),
166
    }
167

168
    return RunRequest(
×
169
        digest=input_digest,
170
        args=[_in_chroot(venv_pex.pex.argv0)],
171
        extra_env=extra_env,
172
        append_only_caches=complete_pex_environment.append_only_caches,
173
    )
174

175

UNCOV
176
def rules():
4✔
UNCOV
177
    return [
4✔
178
        *collect_rules(),
179
        *PythonRequirementFieldSet.rules(),
180
    ]
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