• 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/codegen/thrift/apache/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
import logging
×
UNCOV
6
from dataclasses import dataclass
×
7

UNCOV
8
from pants.backend.codegen.thrift.apache.subsystem import ApacheThriftSubsystem
×
UNCOV
9
from pants.backend.codegen.thrift.target_types import ThriftSourceField
×
UNCOV
10
from pants.core.environments.target_types import EnvironmentTarget
×
UNCOV
11
from pants.core.util_rules.env_vars import environment_vars_subset
×
UNCOV
12
from pants.core.util_rules.source_files import SourceFilesRequest, determine_source_files
×
UNCOV
13
from pants.core.util_rules.system_binaries import (
×
14
    BinaryNotFoundError,
15
    BinaryPathRequest,
16
    BinaryPathTest,
17
    find_binary,
18
)
UNCOV
19
from pants.engine.env_vars import EnvironmentVarsRequest
×
UNCOV
20
from pants.engine.fs import CreateDigest, Directory, MergeDigests, RemovePrefix, Snapshot
×
UNCOV
21
from pants.engine.internals.graph import transitive_targets as transitive_targets_get
×
UNCOV
22
from pants.engine.internals.selectors import concurrently
×
UNCOV
23
from pants.engine.intrinsics import create_digest, digest_to_snapshot, merge_digests
×
UNCOV
24
from pants.engine.process import Process, execute_process_or_raise
×
UNCOV
25
from pants.engine.rules import collect_rules, implicitly, rule
×
UNCOV
26
from pants.engine.target import TransitiveTargetsRequest
×
UNCOV
27
from pants.source.source_root import SourceRootsRequest, get_source_roots
×
UNCOV
28
from pants.util.logging import LogLevel
×
UNCOV
29
from pants.util.strutil import bullet_list, softwrap
×
30

UNCOV
31
logger = logging.getLogger(__name__)
×
32

33

UNCOV
34
@dataclass(frozen=True)
×
UNCOV
35
class GenerateThriftSourcesRequest:
×
UNCOV
36
    thrift_source_field: ThriftSourceField
×
UNCOV
37
    lang_id: str
×
UNCOV
38
    lang_options: tuple[str, ...]
×
UNCOV
39
    lang_name: str
×
40

41

UNCOV
42
@dataclass(frozen=True)
×
UNCOV
43
class GeneratedThriftSources:
×
UNCOV
44
    snapshot: Snapshot
×
45

46

UNCOV
47
@dataclass(frozen=True)
×
UNCOV
48
class ApacheThriftSetup:
×
UNCOV
49
    path: str
×
50

51

UNCOV
52
@rule
×
UNCOV
53
async def generate_apache_thrift_sources(
×
54
    request: GenerateThriftSourcesRequest,
55
    thrift: ApacheThriftSetup,
56
) -> GeneratedThriftSources:
57
    output_dir = "_generated_files"
×
58

59
    transitive_targets, empty_output_dir_digest = await concurrently(
×
60
        transitive_targets_get(
61
            TransitiveTargetsRequest([request.thrift_source_field.address]), **implicitly()
62
        ),
63
        create_digest(CreateDigest([Directory(output_dir)])),
64
    )
65

66
    transitive_sources, target_sources = await concurrently(
×
67
        determine_source_files(
68
            SourceFilesRequest(
69
                tgt[ThriftSourceField]
70
                for tgt in transitive_targets.closure
71
                if tgt.has_field(ThriftSourceField)
72
            )
73
        ),
74
        determine_source_files(SourceFilesRequest([request.thrift_source_field])),
75
    )
76

77
    sources_roots = await get_source_roots(
×
78
        SourceRootsRequest.for_files(transitive_sources.snapshot.files)
79
    )
80
    deduped_source_root_paths = sorted({sr.path for sr in sources_roots.path_to_root.values()})
×
81

82
    input_digest = await merge_digests(
×
83
        MergeDigests(
84
            [
85
                transitive_sources.snapshot.digest,
86
                target_sources.snapshot.digest,
87
                empty_output_dir_digest,
88
            ]
89
        )
90
    )
91

92
    options_str = ""
×
93
    if request.lang_options:
×
94
        options_str = f":{','.join(opt for opt in request.lang_options)}"
×
95

96
    maybe_include_paths = []
×
97
    for path in deduped_source_root_paths:
×
98
        maybe_include_paths.extend(["-I", path])
×
99

100
    args = [
×
101
        thrift.path,
102
        "-out",
103
        output_dir,
104
        *maybe_include_paths,
105
        "--gen",
106
        f"{request.lang_id}{options_str}",
107
        *target_sources.snapshot.files,
108
    ]
109

110
    result = await execute_process_or_raise(
×
111
        **implicitly(
112
            Process(
113
                args,
114
                input_digest=input_digest,
115
                output_directories=(output_dir,),
116
                description=f"Generating {request.lang_name} sources from {request.thrift_source_field.address}.",
117
                level=LogLevel.DEBUG,
118
            )
119
        ),
120
    )
121

122
    output_snapshot = await digest_to_snapshot(
×
123
        **implicitly(RemovePrefix(result.output_digest, output_dir))
124
    )
125
    return GeneratedThriftSources(output_snapshot)
×
126

127

UNCOV
128
@rule
×
UNCOV
129
async def setup_thrift_tool(
×
130
    apache_thrift: ApacheThriftSubsystem,
131
    apache_thrift_env_aware: ApacheThriftSubsystem.EnvironmentAware,
132
    env_target: EnvironmentTarget,
133
) -> ApacheThriftSetup:
134
    env = await environment_vars_subset(EnvironmentVarsRequest(["PATH"]), **implicitly())
×
135
    search_paths = apache_thrift_env_aware.thrift_search_paths(env)
×
136
    all_thrift_binary_paths = await find_binary(
×
137
        BinaryPathRequest(
138
            search_path=search_paths,
139
            binary_name="thrift",
140
            test=BinaryPathTest(["-version"]),
141
        ),
142
        **implicitly(),
143
    )
144
    if not all_thrift_binary_paths.paths:
×
145
        raise BinaryNotFoundError(
×
146
            softwrap(
147
                f"""
148
                Cannot find any `thrift` binaries using the option
149
                `[apache-thrift].thrift_search_paths`: {list(search_paths)}
150

151
                To fix, please install Apache Thrift (https://thrift.apache.org/) with the version
152
                {apache_thrift.expected_version} (set by `[apache-thrift].expected_version`) and ensure
153
                that it is discoverable via `[apache-thrift].thrift_search_paths`.
154
                """
155
            )
156
        )
157

158
    version_results = await concurrently(
×
159
        execute_process_or_raise(
160
            **implicitly(
161
                Process(
162
                    (binary_path.path, "-version"),
163
                    description=f"Determine Apache Thrift version for {binary_path.path}",
164
                    level=LogLevel.DEBUG,
165
                    cache_scope=env_target.executable_search_path_cache_scope(),
166
                )
167
            ),
168
        )
169
        for binary_path in all_thrift_binary_paths.paths
170
    )
171

172
    invalid_versions = []
×
173
    for binary_path, version_result in zip(all_thrift_binary_paths.paths, version_results):
×
174
        try:
×
175
            _raw_version = version_result.stdout.decode("utf-8").split()[2]
×
176
            _version_components = _raw_version.split(".")  # e.g. [1, 17] or [1, 17, 1]
×
177
            version = f"{_version_components[0]}.{_version_components[1]}"
×
178
        except IndexError:
×
179
            raise AssertionError(
×
180
                softwrap(
181
                    f"""
182
                    Failed to parse `thrift -version` output for {binary_path}. Please open a bug at
183
                    https://github.com/pantsbuild/pants/issues/new/choose with the below data:
184

185
                    {version_result}
186
                    """
187
                )
188
            )
189

190
        if version == apache_thrift.expected_version:
×
191
            return ApacheThriftSetup(binary_path.path)
×
192

193
        logger.debug(
×
194
            softwrap(
195
                f"""
196
                The Thrift binary at {binary_path.path} has version {version}, but this
197
                project is using {apache_thrift.expected_version}
198
                (set by `[apache-thrift].expected_version`). Ignoring.
199
                """
200
            )
201
        )
202
        invalid_versions.append((binary_path.path, version))
×
203

204
    invalid_versions_str = bullet_list(
×
205
        f"{path}: {version}" for path, version in sorted(invalid_versions)
206
    )
207
    raise BinaryNotFoundError(
×
208
        softwrap(
209
            f"""
210
            Cannot find a `thrift` binary with the expected version of
211
            {apache_thrift.expected_version} (set by `[apache-thrift].expected_version`).
212

213
            Found these `thrift` binaries, but they had different versions:
214

215
            {invalid_versions_str}
216

217
            To fix, please install the expected version (https://thrift.apache.org/) and ensure
218
            that it is discoverable via the option `[apache-thrift].thrift_search_paths`, or change
219
            `[apache-thrift].expected_version`.
220
            """
221
        )
222
    )
223

224

UNCOV
225
def rules():
×
UNCOV
226
    return collect_rules()
×
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