• 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

41.18
/src/python/pants/option/option_value_container.py
1
# Copyright 2014 Pants project contributors (see CONTRIBUTORS.md).
2
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3

4
from __future__ import annotations
1✔
5

6
import copy
1✔
7
from collections.abc import Iterator
1✔
8
from dataclasses import dataclass
1✔
9

10
from pants.option.ranked_value import Rank, RankedValue, Value
1✔
11

12
Key = str
1✔
13

14

15
class OptionValueContainerBuilder:
1✔
16
    def __init__(self, value_map: dict[Key, RankedValue] | None = None) -> None:
1✔
UNCOV
17
        self._value_map: dict[Key, RankedValue] = value_map if value_map else {}
×
18

19
    def update(self, other: OptionValueContainerBuilder) -> None:
1✔
20
        """Set other's values onto this object.
21

22
        For each key, highest ranked value wins. In a tie, other's value wins.
23
        """
UNCOV
24
        for k, v in other._value_map.items():
×
UNCOV
25
            self._set(k, v)
×
26

27
    def _set(self, key: Key, value: RankedValue) -> None:
1✔
UNCOV
28
        if not isinstance(value, RankedValue):
×
29
            raise AttributeError(f"Value must be of type RankedValue: {value}")
×
30

UNCOV
31
        existing_value = self._value_map.get(key)
×
UNCOV
32
        existing_rank = existing_value.rank if existing_value is not None else Rank.NONE
×
UNCOV
33
        if value.rank >= existing_rank:
×
34
            # We set values from outer scopes before values from inner scopes, so
35
            # in case of equal rank we overwrite. That way that the inner scope value wins.
UNCOV
36
            self._value_map[key] = value
×
37

38
    # Support attribute setting, e.g., opts.foo = RankedValue(Rank.HARDCODED, 42).
39
    def __setattr__(self, key: Key, value: RankedValue) -> None:
1✔
UNCOV
40
        if key == "_value_map":
×
UNCOV
41
            return super().__setattr__(key, value)
×
UNCOV
42
        self._set(key, value)
×
43

44
    def build(self) -> OptionValueContainer:
1✔
UNCOV
45
        return OptionValueContainer(copy.copy(self._value_map))
×
46

47

48
@dataclass(frozen=True)
1✔
49
class OptionValueContainer:
1✔
50
    """A container for option values.
51

52
    Implements "value ranking":
53

54
       Attribute values can be ranked, so that a given attribute's value can only be changed if
55
       the new value has at least as high a rank as the old value. This allows an option value in
56
       an outer scope to override that option's value in an inner scope, when the outer scope's
57
       value comes from a higher ranked source (e.g., the outer value comes from an env var and
58
       the inner one from config).
59

60
       See ranked_value.py for more details.
61
    """
62

63
    _value_map: dict[Key, RankedValue]
1✔
64

65
    def get_keys(self) -> set[Key]:
1✔
66
        return set(self._value_map.keys())
×
67

68
    def get_explicit_keys(self) -> list[Key]:
1✔
69
        """Returns the keys for any values that were set explicitly (via flag, config, or env
70
        var)."""
UNCOV
71
        ret = []
×
UNCOV
72
        for k, v in self._value_map.items():
×
UNCOV
73
            if v.rank > Rank.CONFIG_DEFAULT:
×
UNCOV
74
                ret.append(k)
×
UNCOV
75
        return ret
×
76

77
    def get_rank(self, key: Key) -> Rank:
1✔
78
        """Returns the rank of the value at the specified key.
79

80
        Returns one of the constants in Rank.
81
        """
UNCOV
82
        ranked_value = self._value_map.get(key)
×
UNCOV
83
        if ranked_value is None:
×
84
            raise AttributeError(key)
×
UNCOV
85
        return ranked_value.rank
×
86

87
    def is_flagged(self, key: Key) -> bool:
1✔
88
        """Returns `True` if the value for the specified key was supplied via a flag.
89

90
        A convenience equivalent to `get_rank(key) == Rank.FLAG`.
91

92
        This check can be useful to determine whether or not a user explicitly set an option for this
93
        run.  Although a user might also set an option explicitly via an environment variable, ie via:
94
        `ENV_VAR=value ./pants ...`, this is an ambiguous case since the environment variable could also
95
        be permanently set in the user's environment.
96

97
        :param string key: The name of the option to check.
98
        :returns: `True` if the option was explicitly flagged by the user from the command line.
99
        :rtype: bool
100
        """
UNCOV
101
        return self.get_rank(key) == Rank.FLAG
×
102

103
    def is_default(self, key: Key) -> bool:
1✔
104
        """Returns `True` if the value for the specified key was not supplied by the user.
105

106
        I.e. the option was NOT specified config files, on the cli, or in environment variables.
107

108
        :param string key: The name of the option to check.
109
        :returns: `True` if the user did not set the value for this option.
110
        :rtype: bool
111
        """
UNCOV
112
        return self.get_rank(key) in (Rank.NONE, Rank.HARDCODED)
×
113

114
    def get(self, key: Key, default: Value | None = None):
1✔
115
        # Support dict-like dynamic access.  See also __getitem__ below.
UNCOV
116
        if key not in self._value_map:
×
UNCOV
117
            return default
×
UNCOV
118
        return self._get_underlying_value(key)
×
119

120
    def as_dict(self) -> dict[Key, Value]:
1✔
UNCOV
121
        return {key: self.get(key) for key in self._value_map}
×
122

123
    def to_builder(self) -> OptionValueContainerBuilder:
1✔
124
        return OptionValueContainerBuilder(copy.copy(self._value_map))
×
125

126
    def _get_underlying_value(self, key: Key):
1✔
127
        # Note that the key may exist with a value of None, so we can't just
128
        # test self._value_map.get() for None.
UNCOV
129
        if key not in self._value_map:
×
UNCOV
130
            raise AttributeError(key)
×
UNCOV
131
        ranked_val = self._value_map[key]
×
UNCOV
132
        return ranked_val.value
×
133

134
    # Support natural dynamic access, e.g., opts[foo] is more idiomatic than getattr(opts, 'foo').
135
    def __getitem__(self, key: Key):
1✔
UNCOV
136
        return self.__getattr__(key)
×
137

138
    # Support attribute getting, e.g., foo = opts.foo.
139
    # Note: Called only if regular attribute lookup fails,
140
    # so method and member access will be handled the normal way.
141
    def __getattr__(self, key: Key):
1✔
UNCOV
142
        if key == "_value_map":
×
143
            # In case we get called in copy, which doesn't invoke the ctor.
144
            raise AttributeError(key)
×
UNCOV
145
        return self._get_underlying_value(key)
×
146

147
    def __contains__(self, key: Key):
1✔
148
        return key in self._value_map
×
149

150
    def __iter__(self) -> Iterator[Key]:
1✔
151
        """Returns an iterator over all option names, in lexicographical order."""
UNCOV
152
        yield from sorted(self._value_map.keys())
×
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