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

pantsbuild / pants / 18198316586

02 Oct 2025 03:50PM UTC coverage: 78.82% (-1.4%) from 80.265%
18198316586

push

github

web-flow
Bump serde from 1.0.226 to 1.0.228 in /src/rust (#22723)

Bumps [serde](https://github.com/serde-rs/serde) from 1.0.226 to
1.0.228.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/serde/releases">serde's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.228</h2>
<ul>
<li>Allow building documentation with
<code>RUSTDOCFLAGS='--cfg=docsrs'</code> set for the whole dependency
graph (<a
href="https://redirect.github.com/serde-rs/serde/issues/2995">#2995</a>)</li>
</ul>
<h2>v1.0.227</h2>
<ul>
<li>Documentation improvements (<a
href="https://redirect.github.com/serde-rs/serde/issues/2991">#2991</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/serde-rs/serde/commit/a866b336f"><code>a866b33</code></a>
Release 1.0.228</li>
<li><a
href="https://github.com/serde-rs/serde/commit/5adc9e816"><code>5adc9e8</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2995">#2995</a>
from dtolnay/rustdocflags</li>
<li><a
href="https://github.com/serde-rs/serde/commit/ab581789f"><code>ab58178</code></a>
Workaround for RUSTDOCFLAGS='--cfg=docsrs'</li>
<li><a
href="https://github.com/serde-rs/serde/commit/415d9fc56"><code>415d9fc</code></a>
Release 1.0.227</li>
<li><a
href="https://github.com/serde-rs/serde/commit/7c58427e1"><code>7c58427</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2991">#2991</a>
from dtolnay/inlinecoredoc</li>
<li><a
href="https://github.com/serde-rs/serde/commit/9d3410e3f"><code>9d3410e</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2992">#2992</a>
from dtolnay/inplaceseed</li>
<li><a
href="https://github.com/serde-rs/serde/commit/2fb6748bf1ff93... (continued)

73576 of 93347 relevant lines covered (78.82%)

2.9 hits per line

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

55.68
/src/python/pants/backend/python/util_rules/local_dists.py
1
# Copyright 2019 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
10✔
5

6
import logging
10✔
7
import shlex
10✔
8
from collections.abc import Iterable
10✔
9
from dataclasses import dataclass
10✔
10

11
from pants.backend.python.subsystems.setuptools import PythonDistributionFieldSet
10✔
12
from pants.backend.python.util_rules.interpreter_constraints import InterpreterConstraints
10✔
13
from pants.backend.python.util_rules.pex import Pex, PexRequest, create_pex
10✔
14
from pants.backend.python.util_rules.pex import rules as pex_rules
10✔
15
from pants.backend.python.util_rules.pex_requirements import PexRequirements
10✔
16
from pants.backend.python.util_rules.python_sources import PythonSourceFiles
10✔
17
from pants.build_graph.address import Address
10✔
18
from pants.core.goals.package import PackageFieldSet, build_package
10✔
19
from pants.core.util_rules import system_binaries
10✔
20
from pants.core.util_rules.source_files import SourceFiles
10✔
21
from pants.core.util_rules.system_binaries import BashBinary, UnzipBinary
10✔
22
from pants.engine.addresses import Addresses
10✔
23
from pants.engine.fs import Digest, DigestSubset, MergeDigests, PathGlobs
10✔
24
from pants.engine.internals.graph import resolve_target
10✔
25
from pants.engine.internals.graph import transitive_targets as transitive_targets_get
10✔
26
from pants.engine.intrinsics import digest_to_snapshot, merge_digests
10✔
27
from pants.engine.process import Process, fallible_to_exec_result_or_raise
10✔
28
from pants.engine.rules import collect_rules, concurrently, implicitly, rule
10✔
29
from pants.engine.target import TransitiveTargetsRequest, WrappedTargetRequest
10✔
30
from pants.util.dirutil import fast_relpath_optional
10✔
31
from pants.util.docutil import doc_url
10✔
32
from pants.util.strutil import softwrap
10✔
33

34
logger = logging.getLogger(__name__)
10✔
35

36

37
@dataclass(frozen=True)
10✔
38
class LocalDistWheels:
10✔
39
    """Contains the wheels isolated from a single local Python distribution."""
40

41
    wheel_paths: tuple[str, ...]
10✔
42
    wheels_digest: Digest
10✔
43
    provided_files: frozenset[str]
10✔
44

45

46
@rule
10✔
47
async def isolate_local_dist_wheels(
10✔
48
    dist_field_set: PythonDistributionFieldSet,
49
    bash: BashBinary,
50
    unzip_binary: UnzipBinary,
51
) -> LocalDistWheels:
52
    dist = await build_package(**implicitly({dist_field_set: PackageFieldSet}))
×
53
    wheels_snapshot = await digest_to_snapshot(
×
54
        **implicitly(DigestSubset(dist.digest, PathGlobs(["**/*.whl"])))
55
    )
56

57
    # A given local dist might build a wheel and an sdist (and maybe other artifacts -
58
    # we don't know what setup command was run...)
59
    # As long as there is a wheel, we can ignore the other artifacts.
60
    artifacts = {(a.relpath or "") for a in dist.artifacts}
×
61
    wheels = [wheel for wheel in wheels_snapshot.files if wheel in artifacts]
×
62

63
    if not wheels:
×
64
        tgt = await resolve_target(
×
65
            WrappedTargetRequest(dist_field_set.address, description_of_origin="<infallible>"),
66
            **implicitly(),
67
        )
68
        logger.warning(
×
69
            softwrap(
70
                f"""
71
                Encountered a dependency on the {tgt.target.alias} target at {dist_field_set.address},
72
                but this target does not produce a Python wheel artifact. Therefore this target's
73
                code will be used directly from sources, without a distribution being built,
74
                and any native extensions in it will not be built.
75

76
                See {doc_url("docs/python/overview/building-distributions")} for details on how to set up a
77
                {tgt.target.alias} target to produce a wheel.
78
                """
79
            )
80
        )
81

82
    wheels_listing_result = await fallible_to_exec_result_or_raise(
×
83
        **implicitly(
84
            Process(
85
                argv=[
86
                    bash.path,
87
                    "-c",
88
                    f"""
89
                set -ex
90
                for f in {" ".join(shlex.quote(f) for f in wheels)}; do
91
                  {unzip_binary.path} -Z1 "$f"
92
                done
93
                """,
94
                ],
95
                input_digest=wheels_snapshot.digest,
96
                description=f"List contents of artifacts produced by {dist_field_set.address}",
97
            )
98
        )
99
    )
100
    provided_files = set(wheels_listing_result.stdout.decode().splitlines())
×
101

102
    return LocalDistWheels(
×
103
        tuple(sorted(wheels)), wheels_snapshot.digest, frozenset(sorted(provided_files))
104
    )
105

106

107
@dataclass(frozen=True)
10✔
108
class LocalDistsPexRequest:
10✔
109
    """Request to build the local dists from the dependency closure of a set of addresses."""
110

111
    addresses: Addresses
10✔
112
    interpreter_constraints: InterpreterConstraints
10✔
113
    # The result will return these with the sources provided by the dists subtracted out.
114
    # This will help the caller prevent sources from appearing twice on sys.path.
115
    sources: PythonSourceFiles
10✔
116

117
    def __init__(
10✔
118
        self,
119
        addresses: Iterable[Address],
120
        *,
121
        interpreter_constraints: InterpreterConstraints,
122
        sources: PythonSourceFiles = PythonSourceFiles.empty(),
123
    ) -> None:
124
        object.__setattr__(self, "addresses", Addresses(addresses))
×
125
        object.__setattr__(self, "interpreter_constraints", interpreter_constraints)
×
126
        object.__setattr__(self, "sources", sources)
×
127

128

129
@dataclass(frozen=True)
10✔
130
class LocalDistsPex:
10✔
131
    """A PEX file containing locally-built dists.
132

133
    Can be consumed from another PEX, e.g., by adding to PEX_PATH.
134

135
    The PEX will only contain locally built dists and not their dependencies. For Pants generated
136
    `setup.py` / `pyproject.toml`, the dependencies will be included in the standard resolve process
137
    that the locally-built dists PEX is adjoined to via PEX_PATH. For hand-made `setup.py` /
138
    `pyproject.toml` with 3rdparty dependencies not hand-mirrored into BUILD file dependencies, this
139
    will lead to issues. See https://github.com/pantsbuild/pants/issues/13587#issuecomment-974863636
140
    for one way to fix this corner which is intentionally punted on for now.
141

142
    Lists the files provided by the dists on sys.path, so they can be subtracted from
143
    sources digests, to prevent the same file ending up on sys.path twice.
144
    """
145

146
    pex: Pex
10✔
147
    # The sources from the request, but with any files provided by the local dists subtracted out.
148
    remaining_sources: PythonSourceFiles
10✔
149

150

151
@rule(desc="Building local distributions")
10✔
152
async def build_local_dists(
10✔
153
    request: LocalDistsPexRequest,
154
) -> LocalDistsPex:
155
    transitive_targets = await transitive_targets_get(
×
156
        TransitiveTargetsRequest(request.addresses), **implicitly()
157
    )
158
    applicable_targets = [
×
159
        tgt for tgt in transitive_targets.closure if PythonDistributionFieldSet.is_applicable(tgt)
160
    ]
161

162
    local_dists_wheels = await concurrently(
×
163
        isolate_local_dist_wheels(PythonDistributionFieldSet.create(target), **implicitly())
164
        for target in applicable_targets
165
    )
166

167
    # The primary use-case of the "local dists" feature is to support consuming native extensions
168
    # as wheels without having to publish them first.
169
    # It doesn't seem very useful to consume locally-built sdists, and it makes it hard to
170
    # reason about possible sys.path collisions between the in-repo sources and whatever the
171
    # sdist will place on the sys.path when it's installed.
172
    # So for now we simply ignore sdists, with a warning if necessary.
173
    provided_files: set[str] = set()
×
174
    wheels: list[str] = []
×
175
    wheels_digests = []
×
176
    for local_dist_wheels in local_dists_wheels:
×
177
        wheels.extend(local_dist_wheels.wheel_paths)
×
178
        wheels_digests.append(local_dist_wheels.wheels_digest)
×
179
        provided_files.update(local_dist_wheels.provided_files)
×
180

181
    wheels_digest = await merge_digests(MergeDigests(wheels_digests))
×
182

183
    dists_pex = await create_pex(
×
184
        PexRequest(
185
            output_filename="local_dists.pex",
186
            requirements=PexRequirements(wheels),
187
            interpreter_constraints=request.interpreter_constraints,
188
            additional_inputs=wheels_digest,
189
            # a "local dists" PEX is always just for consumption by some downstream Pants process,
190
            # i.e. internal
191
            internal_only=True,
192
            additional_args=["--intransitive"],
193
        )
194
    )
195

196
    if not wheels:
×
197
        # The source calculations below are not (always) cheap, so we skip them if no wheels were
198
        # produced. See https://github.com/pantsbuild/pants/issues/14561 for one possible approach
199
        # to sharing the cost of these calculations.
200
        return LocalDistsPex(dists_pex, request.sources)
×
201

202
    # We check source roots in reverse lexicographic order,
203
    # so we'll find the innermost root that matches.
204
    source_roots = sorted(request.sources.source_roots, reverse=True)
×
205
    remaining_sources = set(request.sources.source_files.files)
×
206
    unrooted_files_set = set(request.sources.source_files.unrooted_files)
×
207
    for source in request.sources.source_files.files:
×
208
        if source not in unrooted_files_set:
×
209
            for source_root in source_roots:
×
210
                source_relpath = fast_relpath_optional(source, source_root)
×
211
                if source_relpath is not None and source_relpath in provided_files:
×
212
                    remaining_sources.remove(source)
×
213
    remaining_sources_snapshot = await digest_to_snapshot(
×
214
        **implicitly(
215
            DigestSubset(
216
                request.sources.source_files.snapshot.digest, PathGlobs(sorted(remaining_sources))
217
            )
218
        )
219
    )
220
    subtracted_sources = PythonSourceFiles(
×
221
        SourceFiles(remaining_sources_snapshot, request.sources.source_files.unrooted_files),
222
        request.sources.source_roots,
223
    )
224

225
    return LocalDistsPex(dists_pex, subtracted_sources)
×
226

227

228
def rules():
10✔
229
    return (
9✔
230
        *collect_rules(),
231
        *pex_rules(),
232
        *system_binaries.rules(),
233
    )
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