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

pantsbuild / pants / 18198316586

02 Oct 2025 03:50PM UTC coverage: 78.82% (-1.4%) from 80.265%
18198316586

push

github

web-flow
Bump serde from 1.0.226 to 1.0.228 in /src/rust (#22723)

Bumps [serde](https://github.com/serde-rs/serde) from 1.0.226 to
1.0.228.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/serde-rs/serde/releases">serde's
releases</a>.</em></p>
<blockquote>
<h2>v1.0.228</h2>
<ul>
<li>Allow building documentation with
<code>RUSTDOCFLAGS='--cfg=docsrs'</code> set for the whole dependency
graph (<a
href="https://redirect.github.com/serde-rs/serde/issues/2995">#2995</a>)</li>
</ul>
<h2>v1.0.227</h2>
<ul>
<li>Documentation improvements (<a
href="https://redirect.github.com/serde-rs/serde/issues/2991">#2991</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/serde-rs/serde/commit/a866b336f"><code>a866b33</code></a>
Release 1.0.228</li>
<li><a
href="https://github.com/serde-rs/serde/commit/5adc9e816"><code>5adc9e8</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2995">#2995</a>
from dtolnay/rustdocflags</li>
<li><a
href="https://github.com/serde-rs/serde/commit/ab581789f"><code>ab58178</code></a>
Workaround for RUSTDOCFLAGS='--cfg=docsrs'</li>
<li><a
href="https://github.com/serde-rs/serde/commit/415d9fc56"><code>415d9fc</code></a>
Release 1.0.227</li>
<li><a
href="https://github.com/serde-rs/serde/commit/7c58427e1"><code>7c58427</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2991">#2991</a>
from dtolnay/inlinecoredoc</li>
<li><a
href="https://github.com/serde-rs/serde/commit/9d3410e3f"><code>9d3410e</code></a>
Merge pull request <a
href="https://redirect.github.com/serde-rs/serde/issues/2992">#2992</a>
from dtolnay/inplaceseed</li>
<li><a
href="https://github.com/serde-rs/serde/commit/2fb6748bf1ff93... (continued)

73576 of 93347 relevant lines covered (78.82%)

2.9 hits per line

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

75.0
/src/python/pants/util/frozendict.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
10✔
5

6
from collections.abc import Callable, Iterable, Iterator, Mapping
10✔
7
from typing import Any, TypeVar, cast, overload
10✔
8

9
from pants.util.memo import memoized_method
10✔
10
from pants.util.strutil import softwrap
10✔
11

12
K = TypeVar("K")
10✔
13
V = TypeVar("V")
10✔
14

15

16
class FrozenDict(Mapping[K, V]):
10✔
17
    """A wrapper around a normal `dict` that removes all methods to mutate the instance and that
18
    implements __hash__.
19

20
    This should be used instead of normal dicts when working with the engine because normal dicts
21
    are not safe to use.
22
    """
23

24
    @overload
25
    def __init__(self, __items: Iterable[tuple[K, V]], **kwargs: V) -> None: ...
26

27
    @overload
28
    def __init__(self, __other: Mapping[K, V], **kwargs: V) -> None: ...
29

30
    @overload
31
    def __init__(self, **kwargs: V) -> None: ...
32

33
    def __init__(self, *item: Mapping[K, V] | Iterable[tuple[K, V]], **kwargs: V) -> None:
10✔
34
        """Creates a `FrozenDict` with arguments accepted by `dict` that also must be hashable."""
35
        if len(item) > 1:
10✔
36
            raise ValueError(
×
37
                f"{type(self).__name__} was called with {len(item)} positional arguments but it expects one."
38
            )
39

40
        # NB: Keep the variable name `_data` in sync with `externs/mod.rs`.
41
        self._data = dict(item[0]) if item else dict()
10✔
42
        self._data.update(**kwargs)
10✔
43

44
        # NB: We eagerly compute the hash to validate that the values are hashable and to avoid
45
        # performing the calculation multiple times. This can be revisited if it's found to be a
46
        # performance bottleneck.
47
        self._hash = self._calculate_hash()
10✔
48

49
    @classmethod
10✔
50
    def deep_freeze(cls, data: Mapping[K, V]) -> FrozenDict[K, V]:
10✔
51
        """Convert mutable values to their frozen counter parts.
52

53
        Sets and lists are turned into tuples and dicts into FrozenDicts.
54
        """
55

56
        def _freeze(obj):
6✔
57
            if isinstance(obj, dict):
5✔
58
                return cls.deep_freeze(obj)
1✔
59
            if isinstance(obj, (list, set)):
5✔
60
                return tuple(map(_freeze, obj))
4✔
61
            return obj
5✔
62

63
        return cls({k: _freeze(v) for k, v in data.items()})
6✔
64

65
    @staticmethod
10✔
66
    def frozen(to_freeze: Mapping[K, V]) -> FrozenDict[K, V]:
10✔
67
        """Returns a `FrozenDict` containing the keys and values of `to_freeze`.
68

69
        If `to_freeze` is already a `FrozenDict`, returns the same object.
70
        """
71

72
        return to_freeze if isinstance(to_freeze, FrozenDict) else FrozenDict(to_freeze)
×
73

74
    def __getitem__(self, k: K) -> V:
10✔
75
        return self._data[k]
10✔
76

77
    def __len__(self) -> int:
10✔
78
        return len(self._data)
10✔
79

80
    def __iter__(self) -> Iterator[K]:
10✔
81
        return iter(self._data)
10✔
82

83
    def __reversed__(self) -> Iterator[K]:
10✔
84
        return reversed(tuple(self._data))
×
85

86
    def __eq__(self, other: Any) -> Any:
10✔
87
        # defer to dict's __eq__
88
        return self._data == other
10✔
89

90
    def __lt__(self, other: Any) -> bool:
10✔
91
        if not isinstance(other, FrozenDict):
×
92
            return NotImplemented
×
93
        # If sorting each of these on every __lt__ call ends up being a problem we could consider
94
        # optimising this, by, for instance, sorting on construction.
95
        return sorted(self._data.items()) < sorted(other._data.items())
×
96

97
    def __or__(self, other: Any) -> FrozenDict[K, V]:
10✔
98
        if isinstance(other, FrozenDict):
×
99
            other = other._data
×
100
        elif not isinstance(other, Mapping):
×
101
            return NotImplemented
×
102
        return FrozenDict(self._data | other)
×
103

104
    def __ror__(self, other: Any) -> FrozenDict[K, V]:
10✔
105
        if isinstance(other, FrozenDict):
×
106
            other = other._data
×
107
        elif not isinstance(other, Mapping):
×
108
            return NotImplemented
×
109
        return FrozenDict(other | self._data)
×
110

111
    def _calculate_hash(self) -> int:
10✔
112
        try:
10✔
113
            h = 0
10✔
114
            for pair in self._data.items():
10✔
115
                # xor is commutative, i.e. we get the same hash no matter the order of items. This
116
                # "relies" on "hash" of the individual elements being unpredictable enough that such
117
                # a naive aggregation is okay. In addition, the Python hash isn't / shouldn't be
118
                # used for cryptographically sensitive purposes.
119
                h ^= hash(pair)
10✔
120
            return h
10✔
121
        except TypeError as e:
×
122
            raise TypeError(
×
123
                softwrap(
124
                    f"""
125
                    Even though you are using a `{type(self).__name__}`, the underlying values are
126
                    not hashable. Please use hashable (and preferably immutable) types for the
127
                    underlying values, e.g. use tuples instead of lists and use FrozenOrderedSet
128
                    instead of set().
129

130
                    Original error message: {e}
131

132
                    Value: {self}
133
                    """
134
                )
135
            )
136

137
    def __hash__(self) -> int:
10✔
138
        return self._hash
10✔
139

140
    def __repr__(self) -> str:
10✔
141
        return f"{type(self).__name__}({self._data!r})"
10✔
142

143

144
class LazyFrozenDict(FrozenDict[K, V]):
10✔
145
    """A lazy version of `FrozenDict` where the values are not loaded until referenced."""
146

147
    @overload
148
    def __init__(
149
        self, __items: Iterable[tuple[K, Callable[[], V]]], **kwargs: Callable[[], V]
150
    ) -> None: ...
151

152
    @overload
153
    def __init__(self, __other: Mapping[K, Callable[[], V]], **kwargs: Callable[[], V]) -> None: ...
154

155
    @overload
156
    def __init__(self, **kwargs: Callable[[], V]) -> None: ...
157

158
    def __init__(
10✔
159
        self,
160
        *item: Mapping[K, Callable[[], V]] | Iterable[tuple[K, Callable[[], V]]],
161
        **kwargs: Callable[[], V],
162
    ) -> None:
163
        super().__init__(*item, **kwargs)  # type: ignore[arg-type]
2✔
164

165
    def __getitem__(self, k: K) -> V:
10✔
166
        return self._get_value(k)
2✔
167

168
    @memoized_method
10✔
169
    def _get_value(self, k: K) -> V:
10✔
170
        return cast("Callable[[], V]", self._data[k])()
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