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

pantsbuild / pants / 24055979590

06 Apr 2026 11:17PM UTC coverage: 52.37% (-40.5%) from 92.908%
24055979590

Pull #23225

github

web-flow
Merge 67474653c into 542ca048d
Pull Request #23225: Add --test-show-all-batch-targets to expose all targets in batched pytest

6 of 17 new or added lines in 2 files covered. (35.29%)

23030 existing lines in 605 files now uncovered.

31643 of 60422 relevant lines covered (52.37%)

1.05 hits per line

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

83.56
/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

4
from __future__ import annotations
2✔
5

6
from dataclasses import dataclass
2✔
7

8
import yaml
2✔
9

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

40

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

46

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

51

52
class InvalidNfpmContentFileTargetsException(Exception):
2✔
53
    pass
2✔
54

55

56
class NfpmSrcMissingFromSandboxException(Exception):
2✔
57
    pass
2✔
58

59

60
@rule(level=LogLevel.DEBUG)
2✔
61
async def generate_nfpm_yaml(
2✔
62
    request: NfpmPackageConfigRequest,
63
    nfpm_env_aware: NfpmSubsystem.EnvironmentAware,
64
    union_membership: UnionMembership,
65
) -> NfpmPackageConfig:
66
    transitive_targets = await transitive_targets_get(
2✔
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
2✔
78

79
    # Fist get the config that can be constructed from the target itself.
80
    nfpm_package_target = transitive_targets.roots[0]
2✔
81
    injected_fields = await determine_injected_nfpm_package_fields(
2✔
82
        NfpmPackageTargetWrapper(nfpm_package_target), union_membership
83
    )
84
    config = request.field_set.nfpm_config(
2✔
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"]
2✔
90

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

96
    invalid_content_file_addresses = []
2✔
97
    src_missing_from_sandbox_addresses = []
2✔
98

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

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

118
    if invalid_content_file_addresses:
2✔
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:
2✔
UNCOV
133
        plural = len(src_missing_from_sandbox_addresses) > 1
×
UNCOV
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"])
2✔
150

151
    script_src_missing_from_sandbox = {
2✔
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:
2✔
UNCOV
157
        plural = len(script_src_missing_from_sandbox) > 1
×
UNCOV
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"
2✔
174
    nfpm_yaml += yaml.safe_dump(config)
2✔
175
    nfpm_yaml_content = FileContent("nfpm.yaml", nfpm_yaml.encode("utf-8"))
2✔
176

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

180

181
def rules():
2✔
182
    return [*collect_rules()]
2✔
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