Source code for mwparserfromhell.smart_list.list_proxy

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

import weakref

from .utils import _SliceNormalizerMixIn, inheritdoc


[docs] class ListProxy(_SliceNormalizerMixIn, list): """Implement the ``list`` interface by getting elements from a parent. This is created by a :class:`.SmartList` object when slicing. It does not actually store the list at any time; instead, whenever the list is needed, it builds it dynamically using the :meth:`_render` method. """ __slots__ = ("__weakref__", "_parent", "_sliceinfo") def __init__(self, parent, sliceinfo): super().__init__() self._parent = parent self._sliceinfo = sliceinfo def __reduce_ex__(self, protocol: int) -> tuple: return (ListProxy, (self._parent, self._sliceinfo), ()) def __setstate__(self, state: tuple) -> None: # Reregister with the parent child_ref = weakref.ref(self, self._parent._delete_child) self._parent._children[id(child_ref)] = (child_ref, self._sliceinfo) def __repr__(self): return repr(self._render()) def __lt__(self, other): if isinstance(other, ListProxy): return self._render() < list(other) return self._render() < other def __le__(self, other): if isinstance(other, ListProxy): return self._render() <= list(other) return self._render() <= other def __eq__(self, other): if isinstance(other, ListProxy): return self._render() == list(other) return self._render() == other def __ne__(self, other): if isinstance(other, ListProxy): return self._render() != list(other) return self._render() != other def __gt__(self, other): if isinstance(other, ListProxy): return self._render() > list(other) return self._render() > other def __ge__(self, other): if isinstance(other, ListProxy): return self._render() >= list(other) return self._render() >= other def __bool__(self): return bool(self._render()) def __len__(self): return max((self._stop - self._start) // self._step, 0) def __getitem__(self, key): if isinstance(key, slice): key = self._normalize_slice(key, clamp=True) keystart = min(self._start + key.start, self._stop) keystop = min(self._start + key.stop, self._stop) adjusted = slice(keystart, keystop, key.step) return self._parent[adjusted] return self._render()[key] def __setitem__(self, key, item): if isinstance(key, slice): key = self._normalize_slice(key, clamp=True) keystart = min(self._start + key.start, self._stop) keystop = min(self._start + key.stop, self._stop) adjusted = slice(keystart, keystop, key.step) self._parent[adjusted] = item else: length = len(self) if key < 0: key = length + key if key < 0 or key >= length: raise IndexError("list assignment index out of range") self._parent[self._start + key] = item def __delitem__(self, key): if isinstance(key, slice): key = self._normalize_slice(key, clamp=True) keystart = min(self._start + key.start, self._stop) keystop = min(self._start + key.stop, self._stop) adjusted = slice(keystart, keystop, key.step) del self._parent[adjusted] else: length = len(self) if key < 0: key = length + key if key < 0 or key >= length: raise IndexError("list assignment index out of range") del self._parent[self._start + key] def __iter__(self): i = self._start while i < self._stop: yield self._parent[i] i += self._step def __reversed__(self): i = self._stop - 1 while i >= self._start: yield self._parent[i] i -= self._step def __contains__(self, item): return item in self._render() def __add__(self, other): return type(self._parent)(list(self) + other) def __radd__(self, other): return type(self._parent)(other + list(self)) def __iadd__(self, other): self.extend(other) return self def __mul__(self, other): return type(self._parent)(list(self) * other) def __rmul__(self, other): return type(self._parent)(other * list(self)) def __imul__(self, other): self.extend(list(self) * (other - 1)) return self @property def _start(self): """The starting index of this list, inclusive.""" return self._sliceinfo[0] @property def _stop(self): """The ending index of this list, exclusive.""" if self._sliceinfo[1] is None: return len(self._parent) return self._sliceinfo[1] @property def _step(self): """The number to increase the index by between items.""" return self._sliceinfo[2] def _render(self): """Return the actual list from the stored start/stop/step.""" return list(self._parent)[self._start : self._stop : self._step]
[docs] @inheritdoc def append(self, item): self._parent.insert(self._stop, item)
[docs] @inheritdoc def count(self, item): return self._render().count(item)
[docs] @inheritdoc def index(self, item, start=None, stop=None): if start is not None: if stop is not None: return self._render().index(item, start, stop) return self._render().index(item, start) return self._render().index(item)
[docs] @inheritdoc def extend(self, item): self._parent[self._stop : self._stop] = item
[docs] @inheritdoc def insert(self, index, item): if index < 0: index = len(self) + index self._parent.insert(self._start + index, item)
[docs] @inheritdoc def pop(self, index=None): length = len(self) if index is None: index = length - 1 elif index < 0: index = length + index if index < 0 or index >= length: raise IndexError("pop index out of range") return self._parent.pop(self._start + index)
[docs] @inheritdoc def remove(self, item): index = self.index(item) del self._parent[self._start + index]
[docs] @inheritdoc def reverse(self): item = self._render() item.reverse() self._parent[self._start : self._stop : self._step] = item
[docs] @inheritdoc def sort(self, key=None, reverse=None): item = self._render() kwargs = {} if key is not None: kwargs["key"] = key if reverse is not None: kwargs["reverse"] = reverse item.sort(**kwargs) self._parent[self._start : self._stop : self._step] = item