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

earwig / mwparserfromhell / 15949141982

28 Jun 2025 11:18PM UTC coverage: 98.886% (-0.3%) from 99.204%
15949141982

push

github

earwig
Fix a failing test

3106 of 3141 relevant lines covered (98.89%)

9.85 hits per line

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

98.81
/src/mwparserfromhell/smart_list/list_proxy.py
1
# Copyright (C) 2012-2025 Ben Kurtovic <ben.kurtovic@gmail.com>
2
# Copyright (C) 2019-2020 Yuri Astrakhan <YuriAstrakhan@gmail.com>
3
#
4
# Permission is hereby granted, free of charge, to any person obtaining a copy
5
# of this software and associated documentation files (the "Software"), to deal
6
# in the Software without restriction, including without limitation the rights
7
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
# copies of the Software, and to permit persons to whom the Software is
9
# furnished to do so, subject to the following conditions:
10
#
11
# The above copyright notice and this permission notice shall be included in
12
# all copies or substantial portions of the Software.
13
#
14
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
# SOFTWARE.
21

22
from __future__ import annotations
10✔
23

24
import weakref
10✔
25
from typing import Any
10✔
26

27
from .utils import _SliceNormalizerMixIn, inheritdoc
10✔
28

29

30
class ListProxy(_SliceNormalizerMixIn, list):
10✔
31
    """Implement the ``list`` interface by getting elements from a parent.
32

33
    This is created by a :class:`.SmartList` object when slicing. It does not
34
    actually store the list at any time; instead, whenever the list is needed,
35
    it builds it dynamically using the :meth:`_render` method.
36
    """
37

38
    __slots__ = ("__weakref__", "_parent", "_sliceinfo")
10✔
39

40
    def __init__(self, parent, sliceinfo):
10✔
41
        super().__init__()
10✔
42
        self._parent = parent
10✔
43
        self._sliceinfo = sliceinfo
10✔
44

45
    def __reduce_ex__(self, protocol: Any) -> tuple:
10✔
46
        return (ListProxy, (self._parent, self._sliceinfo), ())
10✔
47

48
    def __setstate__(self, state: tuple) -> None:
10✔
49
        # Reregister with the parent
50
        child_ref = weakref.ref(self, self._parent._delete_child)
10✔
51
        self._parent._children[id(child_ref)] = (child_ref, self._sliceinfo)
10✔
52

53
    def __repr__(self):
10✔
54
        return repr(self._render())
10✔
55

56
    def __lt__(self, other):
10✔
57
        if isinstance(other, ListProxy):
10✔
58
            return self._render() < list(other)
10✔
59
        return self._render() < other
10✔
60

61
    def __le__(self, other):
10✔
62
        if isinstance(other, ListProxy):
10✔
63
            return self._render() <= list(other)
10✔
64
        return self._render() <= other
10✔
65

66
    def __eq__(self, other):
10✔
67
        if isinstance(other, ListProxy):
10✔
68
            return self._render() == list(other)
10✔
69
        return self._render() == other
10✔
70

71
    def __ne__(self, other):
10✔
72
        if isinstance(other, ListProxy):
10✔
73
            return self._render() != list(other)
10✔
74
        return self._render() != other
10✔
75

76
    def __gt__(self, other):
10✔
77
        if isinstance(other, ListProxy):
10✔
78
            return self._render() > list(other)
×
79
        return self._render() > other
10✔
80

81
    def __ge__(self, other):
10✔
82
        if isinstance(other, ListProxy):
10✔
83
            return self._render() >= list(other)
×
84
        return self._render() >= other
10✔
85

86
    def __bool__(self):
10✔
87
        return bool(self._render())
10✔
88

89
    def __len__(self):
10✔
90
        return max((self._stop - self._start) // self._step, 0)
10✔
91

92
    def __getitem__(self, key):
10✔
93
        if isinstance(key, slice):
10✔
94
            key = self._normalize_slice(key, clamp=True)
10✔
95
            keystart = min(self._start + key.start, self._stop)
10✔
96
            keystop = min(self._start + key.stop, self._stop)
10✔
97
            adjusted = slice(keystart, keystop, key.step)
10✔
98
            return self._parent[adjusted]
10✔
99
        return self._render()[key]
10✔
100

101
    def __setitem__(self, key, item):
10✔
102
        if isinstance(key, slice):
10✔
103
            key = self._normalize_slice(key, clamp=True)
10✔
104
            keystart = min(self._start + key.start, self._stop)
10✔
105
            keystop = min(self._start + key.stop, self._stop)
10✔
106
            adjusted = slice(keystart, keystop, key.step)
10✔
107
            self._parent[adjusted] = item
10✔
108
        else:
109
            length = len(self)
10✔
110
            if key < 0:
10✔
111
                key = length + key
10✔
112
            if key < 0 or key >= length:
10✔
113
                raise IndexError("list assignment index out of range")
10✔
114
            self._parent[self._start + key] = item
10✔
115

116
    def __delitem__(self, key):
10✔
117
        if isinstance(key, slice):
10✔
118
            key = self._normalize_slice(key, clamp=True)
10✔
119
            keystart = min(self._start + key.start, self._stop)
10✔
120
            keystop = min(self._start + key.stop, self._stop)
10✔
121
            adjusted = slice(keystart, keystop, key.step)
10✔
122
            del self._parent[adjusted]
10✔
123
        else:
124
            length = len(self)
10✔
125
            if key < 0:
10✔
126
                key = length + key
10✔
127
            if key < 0 or key >= length:
10✔
128
                raise IndexError("list assignment index out of range")
10✔
129
            del self._parent[self._start + key]
10✔
130

131
    def __iter__(self):
10✔
132
        i = self._start
10✔
133
        while i < self._stop:
10✔
134
            yield self._parent[i]
10✔
135
            i += self._step
10✔
136

137
    def __reversed__(self):
10✔
138
        i = self._stop - 1
10✔
139
        while i >= self._start:
10✔
140
            yield self._parent[i]
10✔
141
            i -= self._step
10✔
142

143
    def __contains__(self, item):
10✔
144
        return item in self._render()
10✔
145

146
    def __add__(self, other):
10✔
147
        return type(self._parent)(list(self) + other)
10✔
148

149
    def __radd__(self, other):
10✔
150
        return type(self._parent)(other + list(self))
10✔
151

152
    def __iadd__(self, other):
10✔
153
        self.extend(other)
10✔
154
        return self
10✔
155

156
    def __mul__(self, other):
10✔
157
        return type(self._parent)(list(self) * other)
10✔
158

159
    def __rmul__(self, other):
10✔
160
        return type(self._parent)(other * list(self))
10✔
161

162
    def __imul__(self, other):
10✔
163
        self.extend(list(self) * (other - 1))
10✔
164
        return self
10✔
165

166
    @property
10✔
167
    def _start(self):
10✔
168
        """The starting index of this list, inclusive."""
169
        return self._sliceinfo[0]
10✔
170

171
    @property
10✔
172
    def _stop(self):
10✔
173
        """The ending index of this list, exclusive."""
174
        if self._sliceinfo[1] is None:
10✔
175
            return len(self._parent)
10✔
176
        return self._sliceinfo[1]
10✔
177

178
    @property
10✔
179
    def _step(self):
10✔
180
        """The number to increase the index by between items."""
181
        return self._sliceinfo[2]
10✔
182

183
    def _render(self):
10✔
184
        """Return the actual list from the stored start/stop/step."""
185
        return list(self._parent)[self._start : self._stop : self._step]
10✔
186

187
    @inheritdoc
10✔
188
    def append(self, item):
10✔
189
        self._parent.insert(self._stop, item)
10✔
190

191
    @inheritdoc
10✔
192
    def count(self, item):
10✔
193
        return self._render().count(item)
10✔
194

195
    @inheritdoc
10✔
196
    def index(self, item, start=None, stop=None):
10✔
197
        if start is not None:
10✔
198
            if stop is not None:
10✔
199
                return self._render().index(item, start, stop)
10✔
200
            return self._render().index(item, start)
10✔
201
        return self._render().index(item)
10✔
202

203
    @inheritdoc
10✔
204
    def extend(self, item):
10✔
205
        self._parent[self._stop : self._stop] = item
10✔
206

207
    @inheritdoc
10✔
208
    def insert(self, index, item):
10✔
209
        if index < 0:
10✔
210
            index = len(self) + index
10✔
211
        self._parent.insert(self._start + index, item)
10✔
212

213
    @inheritdoc
10✔
214
    def pop(self, index=None):
10✔
215
        length = len(self)
10✔
216
        if index is None:
10✔
217
            index = length - 1
10✔
218
        elif index < 0:
10✔
219
            index = length + index
10✔
220
        if index < 0 or index >= length:
10✔
221
            raise IndexError("pop index out of range")
10✔
222
        return self._parent.pop(self._start + index)
10✔
223

224
    @inheritdoc
10✔
225
    def remove(self, item):
10✔
226
        index = self.index(item)
10✔
227
        del self._parent[self._start + index]
10✔
228

229
    @inheritdoc
10✔
230
    def reverse(self):
10✔
231
        item = self._render()
10✔
232
        item.reverse()
10✔
233
        self._parent[self._start : self._stop : self._step] = item
10✔
234

235
    @inheritdoc
10✔
236
    def sort(self, *, key=None, reverse=None):
10✔
237
        item = self._render()
10✔
238
        kwargs = {}
10✔
239
        if key is not None:
10✔
240
            kwargs["key"] = key
10✔
241
        if reverse is not None:
10✔
242
            kwargs["reverse"] = reverse
10✔
243
        item.sort(**kwargs)
10✔
244
        self._parent[self._start : self._stop : self._step] = item
10✔
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