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

pantsbuild / pants / 25443604553

06 May 2026 03:05PM UTC coverage: 92.879% (-0.04%) from 92.915%
25443604553

push

github

web-flow
[pants_ng] Scaffolding for a pants_ng mode. (#23319)

In this mode the command line is parsed as an
NG invocation, and dispatched appropriately.

Of course at the moment there are no
implementations to dispatch to. That will follow.

This does expose a new option, `pants_ng` to users. 
There is a big warning not to set it, but we're not trying
to hide that we're working on a new thing, so I am
comfortable with this.

25 of 76 new or added lines in 9 files covered. (32.89%)

1294 existing lines in 76 files now uncovered.

92234 of 99306 relevant lines covered (92.88%)

4.05 hits per line

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

87.14
/src/python/pants/engine/internals/target_adaptor.py
1
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
12✔
5

6
import dataclasses
12✔
7
from dataclasses import dataclass
12✔
8
from typing import Any, final
12✔
9

10
from pants.build_graph.address import Address
12✔
11
from pants.engine.engine_aware import EngineAwareParameter
12✔
12
from pants.util.frozendict import FrozenDict
12✔
13
from pants.util.ordered_set import FrozenOrderedSet
12✔
14
from pants.vcs.hunk import TextBlock
12✔
15

16

17
@dataclass(frozen=True)
12✔
18
class SourceBlock:
12✔
19
    """Block of lines in a file.
20

21
    Lines are 1 indexed, `start` is inclusive, `end` is exclusive.
22

23
    SourceBlock is used to describe a set of source lines that are owned by a Target,
24
    thus it can't be empty, i.e. `start` must be less than `end`.
25
    """
26

27
    start: int
12✔
28
    end: int
12✔
29

30
    def __init__(self, start: int, end: int):
12✔
31
        object.__setattr__(self, "start", start)
2✔
32
        object.__setattr__(self, "end", end)
2✔
33

34
        self.__post_init__()
2✔
35

36
    def __post_init__(self):
12✔
37
        if self.start >= self.end:
2✔
38
            raise ValueError(f"{self.start=} must be less than {self.end=}")
×
39

40
    def __len__(self) -> int:
12✔
41
        return self.end - self.start
×
42

43
    def is_touched_by(self, o: TextBlock) -> bool:
12✔
44
        """Check if the TextBlock touches the SourceBlock.
45

46
        The function behaves similarly to range intersection check, but some edge cases are
47
        different. See test cases for details.
48
        """
49

50
        if o.count == 0:
1✔
51
            start = o.start + 1
1✔
52
            end = start
1✔
53
        else:
54
            start = o.start
1✔
55
            end = o.end
1✔
56

57
        if self.end < start:
1✔
58
            return False
1✔
59
        if end < self.start:
1✔
60
            return False
1✔
61
        return True
1✔
62

63
    @classmethod
12✔
64
    def from_text_block(cls, text_block: TextBlock) -> SourceBlock:
12✔
65
        """Convert (start, count) range to (start, end) range.
66

67
        Useful for unified diff conversion, see
68
        https://www.gnu.org/software/diffutils/manual/html_node/Detailed-Unified.html
69
        """
70
        return cls(start=text_block.start, end=text_block.start + text_block.count)
×
71

72

73
class SourceBlocks(FrozenOrderedSet[SourceBlock]):
12✔
74
    pass
12✔
75

76

77
@dataclass(frozen=True)
12✔
78
class TargetAdaptorRequest(EngineAwareParameter):
12✔
79
    """Lookup the TargetAdaptor for an Address."""
80

81
    address: Address
12✔
82
    description_of_origin: str = dataclasses.field(hash=False, compare=False)
12✔
83

84
    def debug_hint(self) -> str:
12✔
85
        return self.address.spec
×
86

87

88
@final
12✔
89
class TargetAdaptor:
12✔
90
    """A light-weight object to store target information before being converted into the Target
91
    API."""
92

93
    __slots__ = ("type_alias", "name", "kwargs", "description_of_origin", "origin_sources_blocks")
12✔
94

95
    def __init__(
12✔
96
        self,
97
        type_alias: str,
98
        name: str | None,
99
        __description_of_origin__: str,
100
        __origin_sources_blocks__: FrozenDict[str, SourceBlocks] = FrozenDict(),
101
        **kwargs: Any,
102
    ) -> None:
103
        self.type_alias = type_alias
12✔
104
        self.name = name
12✔
105
        try:
12✔
106
            self.kwargs = FrozenDict.deep_freeze(kwargs)
12✔
107
        except TypeError as e:
×
108
            # FrozenDict's ctor eagerly computes its hash. If some kwarg is unhashable it will
109
            # raise a TypeError, which we enhance with the description of origin.
110
            raise TypeError(f"In {__description_of_origin__}: {e}")
×
111
        self.description_of_origin = __description_of_origin__
12✔
112
        self.origin_sources_blocks = __origin_sources_blocks__
12✔
113

114
    def with_new_kwargs(self, **kwargs) -> TargetAdaptor:
12✔
UNCOV
115
        return TargetAdaptor(
1✔
116
            type_alias=self.type_alias,
117
            name=self.name,
118
            __description_of_origin__=self.description_of_origin,
119
            __origin_sources_blocks__=self.origin_sources_blocks,
120
            **kwargs,
121
        )
122

123
    def __repr__(self) -> str:
12✔
124
        maybe_blocks = f", {self.origin_sources_blocks}" if self.origin_sources_blocks else ""
×
125
        return f"TargetAdaptor(type_alias={self.type_alias}, name={self.name}, origin={self.description_of_origin}{maybe_blocks})"
×
126

127
    def __eq__(self, other: Any | TargetAdaptor) -> bool:
12✔
128
        if not isinstance(other, TargetAdaptor):
12✔
129
            return NotImplemented
×
130
        return (
12✔
131
            self.type_alias == other.type_alias
132
            and self.name == other.name
133
            and self.kwargs == other.kwargs
134
        )
135

136
    def __hash__(self) -> int:
12✔
137
        return hash((self.type_alias, self.name, self.kwargs))
12✔
138

139
    @property
12✔
140
    def name_explicitly_set(self) -> bool:
12✔
141
        return self.name is not None
12✔
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