github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/third_party/stdlib/_weakrefset.py (about)

     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  
     5  from '__go__/grumpy' import WeakRefType as ref
     6  
     7  __all__ = ['WeakSet']
     8  
     9  
    10  class _IterationGuard(object):
    11      # This context manager registers itself in the current iterators of the
    12      # weak container, such as to delay all removals until the context manager
    13      # exits.
    14      # This technique should be relatively thread-safe (since sets are).
    15  
    16      def __init__(self, weakcontainer):
    17          # Don't create cycles
    18          self.weakcontainer = ref(weakcontainer)
    19  
    20      def __enter__(self):
    21          w = self.weakcontainer()
    22          if w is not None:
    23              w._iterating.add(self)
    24          return self
    25  
    26      def __exit__(self, e, t, b):
    27          w = self.weakcontainer()
    28          if w is not None:
    29              s = w._iterating
    30              s.remove(self)
    31              if not s:
    32                  w._commit_removals()
    33  
    34  
    35  class WeakSet(object):
    36      def __init__(self, data=None):
    37          self.data = set()
    38          def _remove(item, selfref=ref(self)):
    39              self = selfref()
    40              if self is not None:
    41                  if self._iterating:
    42                      self._pending_removals.append(item)
    43                  else:
    44                      self.data.discard(item)
    45          self._remove = _remove
    46          # A list of keys to be removed
    47          self._pending_removals = []
    48          self._iterating = set()
    49          if data is not None:
    50              self.update(data)
    51  
    52      def _commit_removals(self):
    53          l = self._pending_removals
    54          discard = self.data.discard
    55          while l:
    56              discard(l.pop())
    57  
    58      def __iter__(self):
    59          with _IterationGuard(self):
    60              for itemref in self.data:
    61                  item = itemref()
    62                  if item is not None:
    63                      # Caveat: the iterator will keep a strong reference to
    64                      # `item` until it is resumed or closed.
    65                      yield item
    66  
    67      def __len__(self):
    68          return len(self.data) - len(self._pending_removals)
    69  
    70      def __contains__(self, item):
    71          try:
    72              wr = ref(item)
    73          except TypeError:
    74              return False
    75          return wr in self.data
    76  
    77      def __reduce__(self):
    78          return (self.__class__, (list(self),),
    79                  getattr(self, '__dict__', None))
    80  
    81      __hash__ = None
    82  
    83      def add(self, item):
    84          if self._pending_removals:
    85              self._commit_removals()
    86          self.data.add(ref(item, self._remove))
    87  
    88      def clear(self):
    89          if self._pending_removals:
    90              self._commit_removals()
    91          self.data.clear()
    92  
    93      def copy(self):
    94          return self.__class__(self)
    95  
    96      def pop(self):
    97          if self._pending_removals:
    98              self._commit_removals()
    99          while True:
   100              try:
   101                  itemref = self.data.pop()
   102              except KeyError:
   103                  raise KeyError('pop from empty WeakSet')
   104              item = itemref()
   105              if item is not None:
   106                  return item
   107  
   108      def remove(self, item):
   109          if self._pending_removals:
   110              self._commit_removals()
   111          self.data.remove(ref(item))
   112  
   113      def discard(self, item):
   114          if self._pending_removals:
   115              self._commit_removals()
   116          self.data.discard(ref(item))
   117  
   118      def update(self, other):
   119          if self._pending_removals:
   120              self._commit_removals()
   121          for element in other:
   122              self.add(element)
   123  
   124      def __ior__(self, other):
   125          self.update(other)
   126          return self
   127  
   128      def difference(self, other):
   129          newset = self.copy()
   130          newset.difference_update(other)
   131          return newset
   132      __sub__ = difference
   133  
   134      def difference_update(self, other):
   135          self.__isub__(other)
   136      def __isub__(self, other):
   137          if self._pending_removals:
   138              self._commit_removals()
   139          if self is other:
   140              self.data.clear()
   141          else:
   142              self.data.difference_update(ref(item) for item in other)
   143          return self
   144  
   145      def intersection(self, other):
   146          return self.__class__(item for item in other if item in self)
   147      __and__ = intersection
   148  
   149      def intersection_update(self, other):
   150          self.__iand__(other)
   151      def __iand__(self, other):
   152          if self._pending_removals:
   153              self._commit_removals()
   154          self.data.intersection_update(ref(item) for item in other)
   155          return self
   156  
   157      def issubset(self, other):
   158          return self.data.issubset(ref(item) for item in other)
   159      __le__ = issubset
   160  
   161      def __lt__(self, other):
   162          return self.data < set(ref(item) for item in other)
   163  
   164      def issuperset(self, other):
   165          return self.data.issuperset(ref(item) for item in other)
   166      __ge__ = issuperset
   167  
   168      def __gt__(self, other):
   169          return self.data > set(ref(item) for item in other)
   170  
   171      def __eq__(self, other):
   172          if not isinstance(other, self.__class__):
   173              return NotImplemented
   174          return self.data == set(ref(item) for item in other)
   175  
   176      def __ne__(self, other):
   177          opposite = self.__eq__(other)
   178          if opposite is NotImplemented:
   179              return NotImplemented
   180          return not opposite
   181  
   182      def symmetric_difference(self, other):
   183          newset = self.copy()
   184          newset.symmetric_difference_update(other)
   185          return newset
   186      __xor__ = symmetric_difference
   187  
   188      def symmetric_difference_update(self, other):
   189          self.__ixor__(other)
   190      def __ixor__(self, other):
   191          if self._pending_removals:
   192              self._commit_removals()
   193          if self is other:
   194              self.data.clear()
   195          else:
   196              self.data.symmetric_difference_update(ref(item, self._remove) for item in other)
   197          return self
   198  
   199      def union(self, other):
   200          return self.__class__(e for s in (self, other) for e in s)
   201      __or__ = union
   202  
   203      def isdisjoint(self, other):
   204          return len(self.intersection(other)) == 0