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.
 
 
 
 

163 lines
5.3 KiB

  1. from _weakref import ref
  2. from .ListProxy import _ListProxy
  3. from .utils import _SliceNormalizerMixIn, inheritdoc
  4. from ..compat import py3k
  5. class SmartList(_SliceNormalizerMixIn, list):
  6. """Implements the ``list`` interface with special handling of sublists.
  7. When a sublist is created (by ``list[i:j]``), any changes made to this
  8. list (such as the addition, removal, or replacement of elements) will be
  9. reflected in the sublist, or vice-versa, to the greatest degree possible.
  10. This is implemented by having sublists - instances of the
  11. :class:`._ListProxy` type - dynamically determine their elements by storing
  12. their slice info and retrieving that slice from the parent. Methods that
  13. change the size of the list also change the slice info. For example::
  14. >>> parent = SmartList([0, 1, 2, 3])
  15. >>> parent
  16. [0, 1, 2, 3]
  17. >>> child = parent[2:]
  18. >>> child
  19. [2, 3]
  20. >>> child.append(4)
  21. >>> child
  22. [2, 3, 4]
  23. >>> parent
  24. [0, 1, 2, 3, 4]
  25. """
  26. def __init__(self, iterable=None):
  27. if iterable:
  28. super(SmartList, self).__init__(iterable)
  29. else:
  30. super(SmartList, self).__init__()
  31. self._children = {}
  32. def __getitem__(self, key):
  33. if not isinstance(key, slice):
  34. return super(SmartList, self).__getitem__(key)
  35. key = self._normalize_slice(key, clamp=False)
  36. sliceinfo = [key.start, key.stop, key.step]
  37. child = _ListProxy(self, sliceinfo)
  38. child_ref = ref(child, self._delete_child)
  39. self._children[id(child_ref)] = (child_ref, sliceinfo)
  40. return child
  41. def __setitem__(self, key, item):
  42. if not isinstance(key, slice):
  43. return super(SmartList, self).__setitem__(key, item)
  44. item = list(item)
  45. super(SmartList, self).__setitem__(key, item)
  46. key = self._normalize_slice(key, clamp=True)
  47. diff = len(item) + (key.start - key.stop) // key.step
  48. if not diff:
  49. return
  50. values = self._children.values if py3k else self._children.itervalues
  51. for child, (start, stop, step) in values():
  52. if start > key.stop:
  53. self._children[id(child)][1][0] += diff
  54. if stop is not None and stop >= key.stop:
  55. self._children[id(child)][1][1] += diff
  56. def __delitem__(self, key):
  57. super(SmartList, self).__delitem__(key)
  58. if isinstance(key, slice):
  59. key = self._normalize_slice(key, clamp=True)
  60. else:
  61. key = slice(key, key + 1, 1)
  62. diff = (key.stop - key.start) // key.step
  63. values = self._children.values if py3k else self._children.itervalues
  64. for child, (start, stop, step) in values():
  65. if start > key.start:
  66. self._children[id(child)][1][0] -= diff
  67. if stop is not None and stop >= key.stop:
  68. self._children[id(child)][1][1] -= diff
  69. if not py3k:
  70. def __getslice__(self, start, stop):
  71. return self.__getitem__(slice(start, stop))
  72. def __setslice__(self, start, stop, iterable):
  73. self.__setitem__(slice(start, stop), iterable)
  74. def __delslice__(self, start, stop):
  75. self.__delitem__(slice(start, stop))
  76. def __add__(self, other):
  77. return SmartList(list(self) + other)
  78. def __radd__(self, other):
  79. return SmartList(other + list(self))
  80. def __iadd__(self, other):
  81. self.extend(other)
  82. return self
  83. def _delete_child(self, child_ref):
  84. """Remove a child reference that is about to be garbage-collected."""
  85. del self._children[id(child_ref)]
  86. def _detach_children(self):
  87. """Remove all children and give them independent parent copies."""
  88. children = [val[0] for val in self._children.values()]
  89. for child in children:
  90. child()._parent = list(self)
  91. self._children.clear()
  92. @inheritdoc
  93. def append(self, item):
  94. head = len(self)
  95. self[head:head] = [item]
  96. @inheritdoc
  97. def extend(self, item):
  98. head = len(self)
  99. self[head:head] = item
  100. @inheritdoc
  101. def insert(self, index, item):
  102. self[index:index] = [item]
  103. @inheritdoc
  104. def pop(self, index=None):
  105. if index is None:
  106. index = len(self) - 1
  107. item = self[index]
  108. del self[index]
  109. return item
  110. @inheritdoc
  111. def remove(self, item):
  112. del self[self.index(item)]
  113. @inheritdoc
  114. def reverse(self):
  115. self._detach_children()
  116. super(SmartList, self).reverse()
  117. if py3k:
  118. @inheritdoc
  119. def sort(self, key=None, reverse=None):
  120. self._detach_children()
  121. kwargs = {}
  122. if key is not None:
  123. kwargs["key"] = key
  124. if reverse is not None:
  125. kwargs["reverse"] = reverse
  126. super(SmartList, self).sort(**kwargs)
  127. else:
  128. @inheritdoc
  129. def sort(self, cmp=None, key=None, reverse=None):
  130. self._detach_children()
  131. kwargs = {}
  132. if cmp is not None:
  133. kwargs["cmp"] = cmp
  134. if key is not None:
  135. kwargs["key"] = key
  136. if reverse is not None:
  137. kwargs["reverse"] = reverse
  138. super(SmartList, self).sort(**kwargs)