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.

214 lines
6.2 KiB

  1. # Access WeakSet through the weakref module.
  2. # This code is separated-out because it is needed
  3. # by abc.py to load everything else at startup.
  4. from _weakref import ref
  5. __all__ = ['WeakSet']
  6. class _IterationGuard:
  7. # This context manager registers itself in the current iterators of the
  8. # weak container, such as to delay all removals until the context manager
  9. # exits.
  10. # This technique should be relatively thread-safe (since sets are).
  11. def __init__(self, weakcontainer):
  12. # Don't create cycles
  13. self.weakcontainer = ref(weakcontainer)
  14. def __enter__(self):
  15. w = self.weakcontainer()
  16. if w is not None:
  17. w._iterating.add(self)
  18. return self
  19. def __exit__(self, e, t, b):
  20. w = self.weakcontainer()
  21. if w is not None:
  22. s = w._iterating
  23. s.remove(self)
  24. if not s:
  25. w._commit_removals()
  26. class WeakSet:
  27. def __init__(self, data=None):
  28. self.data = set()
  29. def _remove(item, selfref=ref(self)):
  30. self = selfref()
  31. if self is not None:
  32. if self._iterating:
  33. self._pending_removals.append(item)
  34. else:
  35. self.data.discard(item)
  36. self._remove = _remove
  37. # A list of keys to be removed
  38. self._pending_removals = []
  39. self._iterating = set()
  40. if data is not None:
  41. self.update(data)
  42. def _commit_removals(self):
  43. l = self._pending_removals
  44. discard = self.data.discard
  45. while l:
  46. discard(l.pop())
  47. def __iter__(self):
  48. with _IterationGuard(self):
  49. for itemref in self.data:
  50. item = itemref()
  51. if item is not None:
  52. yield item
  53. def __len__(self):
  54. return sum(x() is not None for x in self.data)
  55. def __contains__(self, item):
  56. try:
  57. wr = ref(item)
  58. except TypeError:
  59. return False
  60. return wr in self.data
  61. def __reduce__(self):
  62. return (self.__class__, (list(self),),
  63. getattr(self, '__dict__', None))
  64. def add(self, item):
  65. if self._pending_removals:
  66. self._commit_removals()
  67. self.data.add(ref(item, self._remove))
  68. def clear(self):
  69. if self._pending_removals:
  70. self._commit_removals()
  71. self.data.clear()
  72. def copy(self):
  73. return self.__class__(self)
  74. def pop(self):
  75. if self._pending_removals:
  76. self._commit_removals()
  77. while True:
  78. try:
  79. itemref = self.data.pop()
  80. except KeyError:
  81. raise KeyError('pop from empty WeakSet')
  82. item = itemref()
  83. if item is not None:
  84. return item
  85. def remove(self, item):
  86. if self._pending_removals:
  87. self._commit_removals()
  88. self.data.remove(ref(item))
  89. def discard(self, item):
  90. if self._pending_removals:
  91. self._commit_removals()
  92. self.data.discard(ref(item))
  93. def update(self, other):
  94. if self._pending_removals:
  95. self._commit_removals()
  96. if isinstance(other, self.__class__):
  97. self.data.update(other.data)
  98. else:
  99. for element in other:
  100. self.add(element)
  101. def __ior__(self, other):
  102. self.update(other)
  103. return self
  104. # Helper functions for simple delegating methods.
  105. def _apply(self, other, method):
  106. if not isinstance(other, self.__class__):
  107. other = self.__class__(other)
  108. newdata = method(other.data)
  109. newset = self.__class__()
  110. newset.data = newdata
  111. return newset
  112. def difference(self, other):
  113. return self._apply(other, self.data.difference)
  114. __sub__ = difference
  115. def difference_update(self, other):
  116. if self._pending_removals:
  117. self._commit_removals()
  118. if self is other:
  119. self.data.clear()
  120. else:
  121. self.data.difference_update(ref(item) for item in other)
  122. def __isub__(self, other):
  123. if self._pending_removals:
  124. self._commit_removals()
  125. if self is other:
  126. self.data.clear()
  127. else:
  128. self.data.difference_update(ref(item) for item in other)
  129. return self
  130. def intersection(self, other):
  131. return self._apply(other, self.data.intersection)
  132. __and__ = intersection
  133. def intersection_update(self, other):
  134. if self._pending_removals:
  135. self._commit_removals()
  136. self.data.intersection_update(ref(item) for item in other)
  137. def __iand__(self, other):
  138. if self._pending_removals:
  139. self._commit_removals()
  140. self.data.intersection_update(ref(item) for item in other)
  141. return self
  142. def issubset(self, other):
  143. return self.data.issubset(ref(item) for item in other)
  144. __lt__ = issubset
  145. def __le__(self, other):
  146. return self.data <= set(ref(item) for item in other)
  147. def issuperset(self, other):
  148. return self.data.issuperset(ref(item) for item in other)
  149. __gt__ = issuperset
  150. def __ge__(self, other):
  151. return self.data >= set(ref(item) for item in other)
  152. def __eq__(self, other):
  153. if not isinstance(other, self.__class__):
  154. return NotImplemented
  155. return self.data == set(ref(item) for item in other)
  156. def symmetric_difference(self, other):
  157. return self._apply(other, self.data.symmetric_difference)
  158. __xor__ = symmetric_difference
  159. def symmetric_difference_update(self, other):
  160. if self._pending_removals:
  161. self._commit_removals()
  162. if self is other:
  163. self.data.clear()
  164. else:
  165. self.data.symmetric_difference_update(ref(item) for item in other)
  166. def __ixor__(self, other):
  167. if self._pending_removals:
  168. self._commit_removals()
  169. if self is other:
  170. self.data.clear()
  171. else:
  172. self.data.symmetric_difference_update(ref(item) for item in other)
  173. return self
  174. def union(self, other):
  175. return self._apply(other, self.data.union)
  176. __or__ = union
  177. def isdisjoint(self, other):
  178. return len(self.intersection(other)) == 0