• 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/core/util_rules/wrap_source.py
1
# Copyright 2022 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3
# This file should be moved once we figure out where everything belongs
4

UNCOV
5
from __future__ import annotations
×
6

UNCOV
7
import logging
×
UNCOV
8
from collections.abc import Iterable
×
UNCOV
9
from dataclasses import dataclass
×
10

UNCOV
11
from pants.core.target_types import FileSourceField
×
UNCOV
12
from pants.core.util_rules.source_files import SourceFilesRequest, determine_source_files
×
UNCOV
13
from pants.engine.addresses import UnparsedAddressInputs
×
UNCOV
14
from pants.engine.fs import DigestSubset, PathGlobs
×
UNCOV
15
from pants.engine.internals.graph import resolve_targets
×
UNCOV
16
from pants.engine.intrinsics import digest_subset_to_digest, digest_to_snapshot
×
UNCOV
17
from pants.engine.rules import Rule, collect_rules, implicitly, rule
×
UNCOV
18
from pants.engine.target import (
×
19
    COMMON_TARGET_FIELDS,
20
    GeneratedSources,
21
    GenerateSourcesRequest,
22
    MultipleSourcesField,
23
    SourcesField,
24
    SpecialCasedDependencies,
25
    StringSequenceField,
26
    Target,
27
)
UNCOV
28
from pants.engine.unions import UnionRule
×
UNCOV
29
from pants.util.strutil import help_text
×
30

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

33

UNCOV
34
@dataclass(frozen=True)
×
UNCOV
35
class WrapSource:
×
UNCOV
36
    rules: tuple[Rule | UnionRule, ...]
×
UNCOV
37
    target_types: tuple[type[Target], ...]
×
38

39

UNCOV
40
class ActivateWrapSourceTargetFieldBase(MultipleSourcesField):
×
41
    # We solely register so that codegen can match a fieldset.
42
    # One unique subclass must be defined per target type.
UNCOV
43
    alias = "_sources"
×
UNCOV
44
    uses_source_roots = False
×
UNCOV
45
    expected_num_files = 0
×
46

47

UNCOV
48
class WrapSourceInputsField(SpecialCasedDependencies):
×
UNCOV
49
    alias = "inputs"
×
UNCOV
50
    required = True
×
UNCOV
51
    help = "The input targets that are to be made available by this target."
×
52

53

UNCOV
54
class WrapSourceOutputsField(StringSequenceField):
×
UNCOV
55
    alias = "outputs"
×
UNCOV
56
    required = False
×
UNCOV
57
    help = help_text(
×
58
        "The output files that are made available in the new context by this target. If not "
59
        "specified, the target will capture all files with the expected extensions for this "
60
        "source format: see the help for the target for the specific extensions. If no extensions "
61
        "are specified and this value is not specified, all input files will be returned."
62
    )
63

64

UNCOV
65
async def _wrap_source(wrapper: GenerateSourcesRequest) -> GeneratedSources:
×
66
    request = wrapper.protocol_target
×
67
    default_extensions = {i for i in (wrapper.output.expected_file_extensions or ()) if i}
×
68

69
    inputs = await resolve_targets(
×
70
        **implicitly(
71
            {request.get(WrapSourceInputsField).to_unparsed_address_inputs(): UnparsedAddressInputs}
72
        )
73
    )
74

75
    sources = await determine_source_files(
×
76
        SourceFilesRequest(
77
            sources_fields=[tgt.get(SourcesField) for tgt in inputs],
78
            for_sources_types=(SourcesField, FileSourceField),
79
            enable_codegen=True,
80
        ),
81
    )
82

83
    outputs_value: Iterable[str] | None = request.get(WrapSourceOutputsField).value
×
84
    if outputs_value:
×
85
        pass
×
86
    elif default_extensions:
×
87
        outputs_value = [i for i in sources.files if any(i.endswith(j) for j in default_extensions)]
×
88
    else:
89
        outputs_value = sources.files
×
90

91
    filter_digest = await digest_subset_to_digest(
×
92
        DigestSubset(sources.snapshot.digest, PathGlobs(outputs_value))
93
    )
94

95
    snapshot = await digest_to_snapshot(filter_digest)
×
96
    return GeneratedSources(snapshot)
×
97

98

UNCOV
99
def wrap_source_rule_and_target(
×
100
    source_field_type: type[SourcesField], target_name_suffix: str
101
) -> WrapSource:
UNCOV
102
    if source_field_type.expected_file_extensions:
×
UNCOV
103
        outputs_help = (
×
104
            "If `outputs` is not specified, all files with the following extensions will be "
105
            "matched: "
106
            + ", ".join(ext for ext in source_field_type.expected_file_extensions if ext)
107
        )
108
    else:
UNCOV
109
        outputs_help = "If `outputs` is not specified, all files from `inputs` will be matched."
×
110

UNCOV
111
    class ActivateWrapSourceTargetField(ActivateWrapSourceTargetFieldBase):
×
UNCOV
112
        pass
×
113

UNCOV
114
    class GenerateWrapSourceSourcesRequest(GenerateSourcesRequest):
×
UNCOV
115
        input = ActivateWrapSourceTargetField
×
UNCOV
116
        output = source_field_type
×
117

UNCOV
118
    setattr(
×
119
        GenerateWrapSourceSourcesRequest,
120
        "__qualname__",
121
        f"{source_field_type.__qualname__}.{GenerateWrapSourceSourcesRequest.__name__}",
122
    )
123

UNCOV
124
    class WrapSourceTarget(Target):
×
UNCOV
125
        alias = f"experimental_wrap_as_{target_name_suffix}"
×
UNCOV
126
        core_fields = (
×
127
            *COMMON_TARGET_FIELDS,
128
            ActivateWrapSourceTargetField,
129
            WrapSourceInputsField,
130
            WrapSourceOutputsField,
131
        )
UNCOV
132
        help = help_text(
×
133
            f"""
134
            Allow files and sources produced by the targets specified by `inputs` to be consumed
135
            by rules that specifically expect a `{source_field_type.__name__}`.
136

137
            Note that this target does not modify the files in any way. {outputs_help}
138

139
            This target must be explicitly specified as a dependency of any target that requires
140
            it. Sources from this target will not be automatically inferred as dependencies.
141

142
            This target is experimental: in future versions of Pants, this functionality may be
143
            made available with a different interface.
144
            """
145
        )
146

147
    # need to use `_param_type_overrides` to stop `@rule` from inspecting the function's source
UNCOV
148
    @rule(
×
149
        canonical_name_suffix=source_field_type.__name__,
150
        _param_type_overrides={"request": GenerateWrapSourceSourcesRequest},
151
    )
UNCOV
152
    async def wrap_source(request: GenerateSourcesRequest) -> GeneratedSources:
×
153
        return await _wrap_source(request)
×
154

UNCOV
155
    return WrapSource(
×
156
        rules=(
157
            *collect_rules(locals()),
158
            UnionRule(GenerateSourcesRequest, GenerateWrapSourceSourcesRequest),
159
        ),
160
        target_types=(WrapSourceTarget,),
161
    )
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