A Python parser for MediaWiki wikicode https://mwparserfromhell.readthedocs.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

241 lines
7.5 KiB

  1. # SmartList has to be a full import in order to avoid cyclical import errors
  2. import mwparserfromhell.smart_list.SmartList
  3. from .utils import _SliceNormalizerMixIn, inheritdoc
  4. from ..compat import py3k
  5. class _ListProxy(_SliceNormalizerMixIn, list):
  6. """Implement the ``list`` interface by getting elements from a parent.
  7. This is created by a :class:`.SmartList` object when slicing. It does not
  8. actually store the list at any time; instead, whenever the list is needed,
  9. it builds it dynamically using the :meth:`_render` method.
  10. """
  11. def __init__(self, parent, sliceinfo):
  12. super(_ListProxy, self).__init__()
  13. self._parent = parent
  14. self._sliceinfo = sliceinfo
  15. def __repr__(self):
  16. return repr(self._render())
  17. def __lt__(self, other):
  18. if isinstance(other, _ListProxy):
  19. return self._render() < list(other)
  20. return self._render() < other
  21. def __le__(self, other):
  22. if isinstance(other, _ListProxy):
  23. return self._render() <= list(other)
  24. return self._render() <= other
  25. def __eq__(self, other):
  26. if isinstance(other, _ListProxy):
  27. return self._render() == list(other)
  28. return self._render() == other
  29. def __ne__(self, other):
  30. if isinstance(other, _ListProxy):
  31. return self._render() != list(other)
  32. return self._render() != other
  33. def __gt__(self, other):
  34. if isinstance(other, _ListProxy):
  35. return self._render() > list(other)
  36. return self._render() > other
  37. def __ge__(self, other):
  38. if isinstance(other, _ListProxy):
  39. return self._render() >= list(other)
  40. return self._render() >= other
  41. if py3k:
  42. def __bool__(self):
  43. return bool(self._render())
  44. else:
  45. def __nonzero__(self):
  46. return bool(self._render())
  47. def __len__(self):
  48. return max((self._stop - self._start) // self._step, 0)
  49. def __getitem__(self, key):
  50. if isinstance(key, slice):
  51. key = self._normalize_slice(key, clamp=True)
  52. keystart = min(self._start + key.start, self._stop)
  53. keystop = min(self._start + key.stop, self._stop)
  54. adjusted = slice(keystart, keystop, key.step)
  55. return self._parent[adjusted]
  56. else:
  57. return self._render()[key]
  58. def __setitem__(self, key, item):
  59. if isinstance(key, slice):
  60. key = self._normalize_slice(key, clamp=True)
  61. keystart = min(self._start + key.start, self._stop)
  62. keystop = min(self._start + key.stop, self._stop)
  63. adjusted = slice(keystart, keystop, key.step)
  64. self._parent[adjusted] = item
  65. else:
  66. length = len(self)
  67. if key < 0:
  68. key = length + key
  69. if key < 0 or key >= length:
  70. raise IndexError("list assignment index out of range")
  71. self._parent[self._start + key] = item
  72. def __delitem__(self, key):
  73. if isinstance(key, slice):
  74. key = self._normalize_slice(key, clamp=True)
  75. keystart = min(self._start + key.start, self._stop)
  76. keystop = min(self._start + key.stop, self._stop)
  77. adjusted = slice(keystart, keystop, key.step)
  78. del self._parent[adjusted]
  79. else:
  80. length = len(self)
  81. if key < 0:
  82. key = length + key
  83. if key < 0 or key >= length:
  84. raise IndexError("list assignment index out of range")
  85. del self._parent[self._start + key]
  86. def __iter__(self):
  87. i = self._start
  88. while i < self._stop:
  89. yield self._parent[i]
  90. i += self._step
  91. def __reversed__(self):
  92. i = self._stop - 1
  93. while i >= self._start:
  94. yield self._parent[i]
  95. i -= self._step
  96. def __contains__(self, item):
  97. return item in self._render()
  98. if not py3k:
  99. def __getslice__(self, start, stop):
  100. return self.__getitem__(slice(start, stop))
  101. def __setslice__(self, start, stop, iterable):
  102. self.__setitem__(slice(start, stop), iterable)
  103. def __delslice__(self, start, stop):
  104. self.__delitem__(slice(start, stop))
  105. def __add__(self, other):
  106. return mwparserfromhell.smart_list.SmartList(list(self) + other)
  107. def __radd__(self, other):
  108. return mwparserfromhell.smart_list.SmartList(other + list(self))
  109. def __iadd__(self, other):
  110. self.extend(other)
  111. return self
  112. def __mul__(self, other):
  113. return mwparserfromhell.smart_list.SmartList(list(self) * other)
  114. def __rmul__(self, other):
  115. return mwparserfromhell.smart_list.SmartList(other * list(self))
  116. def __imul__(self, other):
  117. self.extend(list(self) * (other - 1))
  118. return self
  119. @property
  120. def _start(self):
  121. """The starting index of this list, inclusive."""
  122. return self._sliceinfo[0]
  123. @property
  124. def _stop(self):
  125. """The ending index of this list, exclusive."""
  126. if self._sliceinfo[1] is None:
  127. return len(self._parent)
  128. return self._sliceinfo[1]
  129. @property
  130. def _step(self):
  131. """The number to increase the index by between items."""
  132. return self._sliceinfo[2]
  133. def _render(self):
  134. """Return the actual list from the stored start/stop/step."""
  135. return list(self._parent)[self._start:self._stop:self._step]
  136. @inheritdoc
  137. def append(self, item):
  138. self._parent.insert(self._stop, item)
  139. @inheritdoc
  140. def count(self, item):
  141. return self._render().count(item)
  142. @inheritdoc
  143. def index(self, item, start=None, stop=None):
  144. if start is not None:
  145. if stop is not None:
  146. return self._render().index(item, start, stop)
  147. return self._render().index(item, start)
  148. return self._render().index(item)
  149. @inheritdoc
  150. def extend(self, item):
  151. self._parent[self._stop:self._stop] = item
  152. @inheritdoc
  153. def insert(self, index, item):
  154. if index < 0:
  155. index = len(self) + index
  156. self._parent.insert(self._start + index, item)
  157. @inheritdoc
  158. def pop(self, index=None):
  159. length = len(self)
  160. if index is None:
  161. index = length - 1
  162. elif index < 0:
  163. index = length + index
  164. if index < 0 or index >= length:
  165. raise IndexError("pop index out of range")
  166. return self._parent.pop(self._start + index)
  167. @inheritdoc
  168. def remove(self, item):
  169. index = self.index(item)
  170. del self._parent[self._start + index]
  171. @inheritdoc
  172. def reverse(self):
  173. item = self._render()
  174. item.reverse()
  175. self._parent[self._start:self._stop:self._step] = item
  176. if py3k:
  177. @inheritdoc
  178. def sort(self, key=None, reverse=None):
  179. item = self._render()
  180. kwargs = {}
  181. if key is not None:
  182. kwargs["key"] = key
  183. if reverse is not None:
  184. kwargs["reverse"] = reverse
  185. item.sort(**kwargs)
  186. self._parent[self._start:self._stop:self._step] = item
  187. else:
  188. @inheritdoc
  189. def sort(self, cmp=None, key=None, reverse=None):
  190. item = self._render()
  191. kwargs = {}
  192. if cmp is not None:
  193. kwargs["cmp"] = cmp
  194. if key is not None:
  195. kwargs["key"] = key
  196. if reverse is not None:
  197. kwargs["reverse"] = reverse
  198. item.sort(**kwargs)
  199. self._parent[self._start:self._stop:self._step] = item