• 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/nfpm/util_rules/generate_config.py
1
# Copyright 2023 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

UNCOV
4
from __future__ import annotations
×
5

UNCOV
6
from dataclasses import dataclass
×
7

UNCOV
8
import yaml
×
9

UNCOV
10
from pants.backend.nfpm.config import NfpmContent
×
UNCOV
11
from pants.backend.nfpm.field_sets import (
×
12
    NfpmContentFieldSet,
13
    NfpmContentFileFieldSet,
14
    NfpmPackageFieldSet,
15
)
UNCOV
16
from pants.backend.nfpm.fields.all import NfpmDependencies
×
UNCOV
17
from pants.backend.nfpm.fields.contents import NfpmContentFileSourceField, NfpmContentSrcField
×
UNCOV
18
from pants.backend.nfpm.subsystem import NfpmSubsystem
×
UNCOV
19
from pants.backend.nfpm.target_types import NfpmContentFile
×
UNCOV
20
from pants.backend.nfpm.util_rules.inject_config import (
×
21
    NfpmPackageTargetWrapper,
22
    determine_injected_nfpm_package_fields,
23
)
UNCOV
24
from pants.core.goals.package import TraverseIfNotPackageTarget
×
UNCOV
25
from pants.engine.fs import CreateDigest, FileContent, FileEntry
×
UNCOV
26
from pants.engine.internals.graph import find_valid_field_sets
×
UNCOV
27
from pants.engine.internals.graph import transitive_targets as transitive_targets_get
×
UNCOV
28
from pants.engine.internals.native_engine import Digest
×
UNCOV
29
from pants.engine.intrinsics import create_digest, get_digest_entries
×
UNCOV
30
from pants.engine.rules import collect_rules, implicitly, rule
×
UNCOV
31
from pants.engine.target import (
×
32
    FieldSetsPerTarget,
33
    FieldSetsPerTargetRequest,
34
    TransitiveTargetsRequest,
35
)
UNCOV
36
from pants.engine.unions import UnionMembership
×
UNCOV
37
from pants.util.logging import LogLevel
×
UNCOV
38
from pants.util.strutil import softwrap
×
39

40

UNCOV
41
@dataclass(frozen=True)
×
UNCOV
42
class NfpmPackageConfigRequest:
×
UNCOV
43
    field_set: NfpmPackageFieldSet
×
UNCOV
44
    content_sandbox_digest: Digest  # NfpmContentSandbox.digest
×
45

46

UNCOV
47
@dataclass(frozen=True)
×
UNCOV
48
class NfpmPackageConfig:
×
UNCOV
49
    digest: Digest  # digest contains nfpm.yaml
×
50

51

UNCOV
52
class InvalidNfpmContentFileTargetsException(Exception):
×
UNCOV
53
    pass
×
54

55

UNCOV
56
class NfpmSrcMissingFromSandboxException(Exception):
×
UNCOV
57
    pass
×
58

59

UNCOV
60
@rule(level=LogLevel.DEBUG)
×
UNCOV
61
async def generate_nfpm_yaml(
×
62
    request: NfpmPackageConfigRequest,
63
    nfpm_env_aware: NfpmSubsystem.EnvironmentAware,
64
    union_membership: UnionMembership,
65
) -> NfpmPackageConfig:
66
    transitive_targets = await transitive_targets_get(
×
67
        TransitiveTargetsRequest(
68
            [request.field_set.address],
69
            should_traverse_deps_predicate=TraverseIfNotPackageTarget(
70
                roots=[request.field_set.address],
71
                union_membership=union_membership,
72
            ),
73
        ),
74
        **implicitly(),
75
    )
76

77
    default_mtime = nfpm_env_aware.default_mtime
×
78

79
    # Fist get the config that can be constructed from the target itself.
80
    nfpm_package_target = transitive_targets.roots[0]
×
81
    injected_fields = await determine_injected_nfpm_package_fields(
×
82
        NfpmPackageTargetWrapper(nfpm_package_target), union_membership
83
    )
84
    config = request.field_set.nfpm_config(
×
85
        nfpm_package_target, injected_fields.field_values, default_mtime=default_mtime
86
    )
87

88
    # Second, gather package contents from hydrated deps.
89
    contents: list[NfpmContent] = config["contents"]
×
90

91
    content_sandbox_entries = await get_digest_entries(request.content_sandbox_digest)
×
92
    content_sandbox_files = {
×
93
        entry.path: entry for entry in content_sandbox_entries if isinstance(entry, FileEntry)
94
    }
95

96
    invalid_content_file_addresses = []
×
97
    src_missing_from_sandbox_addresses = []
×
98

99
    content_field_sets: FieldSetsPerTarget = await find_valid_field_sets(
×
100
        FieldSetsPerTargetRequest(NfpmContentFieldSet, transitive_targets.dependencies),
101
        **implicitly(),
102
    )
103

104
    field_set: NfpmContentFieldSet
105
    for field_set in content_field_sets.field_sets:
×
106
        try:
×
107
            nfpm_content = field_set.nfpm_config(
×
108
                content_sandbox_files=content_sandbox_files, default_mtime=default_mtime
109
            )
110
        except NfpmContentFileFieldSet.InvalidTarget:
×
111
            invalid_content_file_addresses.append(field_set.address)
×
112
            continue
×
113
        except NfpmContentFileFieldSet.SrcMissingFomSandbox:
×
114
            src_missing_from_sandbox_addresses.append(field_set.address)
×
115
            continue
×
116
        contents.append(nfpm_content)
×
117

118
    if invalid_content_file_addresses:
×
119
        plural = len(invalid_content_file_addresses) > 1
×
120
        raise InvalidNfpmContentFileTargetsException(
×
121
            softwrap(
122
                f"""
123
                The '{NfpmContentFile.alias}' target type requires a value for the '{NfpmContentSrcField.alias}' field,
124
                But {"these targets are" if plural else "this target is"} missing a '{NfpmContentSrcField.alias}' value.
125
                If the '{NfpmContentFileSourceField.alias}' field is provided, then the '{NfpmContentSrcField.alias}'
126
                defaults to the file referenced in the '{NfpmContentFileSourceField.alias}' field.
127
                Please fix the {"targets at these addresses" if plural else "target at this address"}:
128
                """
129
            )
130
            + "\n".join(str(address) for address in invalid_content_file_addresses)
131
        )
132
    if src_missing_from_sandbox_addresses:
×
133
        plural = len(src_missing_from_sandbox_addresses) > 1
×
134
        raise NfpmSrcMissingFromSandboxException(
×
135
            softwrap(
136
                f"""
137
                The '{NfpmContentSrcField.alias}' {"files are" if plural else "file is"} missing
138
                from the nfpm sandbox. This sandbox contains packages, generated code, and sources
139
                from the '{NfpmDependencies.alias}' field. It also contains any file from the
140
                '{NfpmContentFileSourceField.alias}' field. Please fix the '{NfpmContentFile.alias}'
141
                {"targets at these addresses" if plural else "target at this address"}:
142
                """
143
            )
144
            + "\n".join(str(address) for address in src_missing_from_sandbox_addresses)
145
            + "\n\nThe sandbox contains these files:\n"
146
            + "\n".join(str(path) for path in content_sandbox_files)
147
        )
148

149
    contents.sort(key=lambda d: d["dst"])
×
150

151
    script_src_missing_from_sandbox = {
×
152
        script_type: script_src
153
        for script_type, script_src in request.field_set.scripts.normalized_value.items()
154
        if content_sandbox_files.get(script_src) is None
155
    }
156
    if script_src_missing_from_sandbox:
×
157
        plural = len(script_src_missing_from_sandbox) > 1
×
158
        raise NfpmSrcMissingFromSandboxException(
×
159
            softwrap(
160
                f"""
161
                {request.field_set.address}: {"Some" if plural else "One"} of the files in the
162
                '{request.field_set.scripts.alias}' field {"are" if plural else "is"} missing
163
                from the nfpm sandbox. The sandbox gets populated from the '{NfpmDependencies.alias}'
164
                field. Are you missing {"any dependencies" if plural else "a dependency"}?
165
                Here {"are" if plural else "is"} the missing {"scripts" if plural else "script"}:
166
                {repr(script_src_missing_from_sandbox)}
167
                """
168
            )
169
            + "\n\nThe sandbox contains these files:\n"
170
            + "\n".join(str(path) for path in content_sandbox_files)
171
        )
172

173
    nfpm_yaml = "# Generated by Pantsbuild\n"
×
174
    nfpm_yaml += yaml.safe_dump(config)
×
175
    nfpm_yaml_content = FileContent("nfpm.yaml", nfpm_yaml.encode("utf-8"))
×
176

177
    digest = await create_digest(CreateDigest([nfpm_yaml_content]))
×
178
    return NfpmPackageConfig(digest)
×
179

180

UNCOV
181
def rules():
×
UNCOV
182
    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

© 2026 Coveralls, Inc