cpython/Lib/_weakrefset.py

205 lines
5.8 KiB
Python
Raw Normal View History

# Access WeakSet through the weakref module.
# This code is separated-out because it is needed
# by abc.py to load everything else at startup.
from _weakref import ref
__all__ = ['WeakSet']
class _IterationGuard(object):
# This context manager registers itself in the current iterators of the
# weak container, such as to delay all removals until the context manager
# exits.
# This technique should be relatively thread-safe (since sets are).
def __init__(self, weakcontainer):
# Don't create cycles
self.weakcontainer = ref(weakcontainer)
def __enter__(self):
w = self.weakcontainer()
if w is not None:
w._iterating.add(self)
return self
def __exit__(self, e, t, b):
w = self.weakcontainer()
if w is not None:
s = w._iterating
s.remove(self)
if not s:
w._commit_removals()
class WeakSet(object):
def __init__(self, data=None):
self.data = set()
def _remove(item, selfref=ref(self)):
self = selfref()
if self is not None:
if self._iterating:
self._pending_removals.append(item)
else:
self.data.discard(item)
self._remove = _remove
# A list of keys to be removed
self._pending_removals = []
self._iterating = set()
if data is not None:
self.update(data)
def _commit_removals(self):
l = self._pending_removals
discard = self.data.discard
while l:
discard(l.pop())
def __iter__(self):
with _IterationGuard(self):
for itemref in self.data:
item = itemref()
if item is not None:
# Caveat: the iterator will keep a strong reference to
# `item` until it is resumed or closed.
yield item
def __len__(self):
return len(self.data) - len(self._pending_removals)
def __contains__(self, item):
Merged revisions 86881,86887,86913-86915,86933,86943,86960,86964,86974,86980,86996,87008 via svnmerge from svn+ssh://pythondev@svn.python.org/python/branches/py3k ........ r86881 | georg.brandl | 2010-11-30 08:43:28 +0100 (Di, 30 Nov 2010) | 1 line #10584: fix bad links. ........ r86887 | georg.brandl | 2010-11-30 15:57:54 +0100 (Di, 30 Nov 2010) | 1 line Fix typo. ........ r86913 | georg.brandl | 2010-12-01 16:32:43 +0100 (Mi, 01 Dez 2010) | 1 line Add missing word, and add a better reference to the actual function. ........ r86914 | georg.brandl | 2010-12-01 16:36:33 +0100 (Mi, 01 Dez 2010) | 1 line #10594: fix parameter names in PyList API docs. ........ r86915 | georg.brandl | 2010-12-01 16:44:25 +0100 (Mi, 01 Dez 2010) | 1 line Fix some markup and style in the unittest docs. ........ r86933 | georg.brandl | 2010-12-02 19:02:01 +0100 (Do, 02 Dez 2010) | 1 line #10597: fix Py_SetPythonHome docs by pointing to where the meaning of PYTHONHOME is already documented. ........ r86943 | georg.brandl | 2010-12-02 23:35:25 +0100 (Do, 02 Dez 2010) | 1 line Re-add accidentally removed line. ........ r86960 | georg.brandl | 2010-12-03 08:55:44 +0100 (Fr, 03 Dez 2010) | 1 line #10360: catch TypeError in WeakSet.__contains__, just like WeakKeyDictionary does. ........ r86964 | georg.brandl | 2010-12-03 10:58:38 +0100 (Fr, 03 Dez 2010) | 1 line #10549: fix interface of docclass() for text documenter. ........ r86974 | georg.brandl | 2010-12-03 16:30:09 +0100 (Fr, 03 Dez 2010) | 1 line Markup consistency fixes. ........ r86980 | georg.brandl | 2010-12-03 18:19:27 +0100 (Fr, 03 Dez 2010) | 1 line Fix punctuation. ........ r86996 | georg.brandl | 2010-12-03 20:56:42 +0100 (Fr, 03 Dez 2010) | 1 line Fix indentation. ........ r87008 | georg.brandl | 2010-12-04 10:04:04 +0100 (Sa, 04 Dez 2010) | 1 line Fix typo. ........
2011-02-25 06:39:23 -04:00
try:
wr = ref(item)
except TypeError:
return False
return wr in self.data
def __reduce__(self):
return (self.__class__, (list(self),),
getattr(self, '__dict__', None))
__hash__ = None
def add(self, item):
if self._pending_removals:
self._commit_removals()
self.data.add(ref(item, self._remove))
def clear(self):
if self._pending_removals:
self._commit_removals()
self.data.clear()
def copy(self):
return self.__class__(self)
def pop(self):
if self._pending_removals:
self._commit_removals()
while True:
try:
itemref = self.data.pop()
except KeyError:
raise KeyError('pop from empty WeakSet')
item = itemref()
if item is not None:
return item
def remove(self, item):
if self._pending_removals:
self._commit_removals()
self.data.remove(ref(item))
def discard(self, item):
if self._pending_removals:
self._commit_removals()
self.data.discard(ref(item))
def update(self, other):
if self._pending_removals:
self._commit_removals()
2012-03-04 15:20:34 -04:00
for element in other:
self.add(element)
def __ior__(self, other):
self.update(other)
return self
def difference(self, other):
newset = self.copy()
newset.difference_update(other)
return newset
__sub__ = difference
def difference_update(self, other):
self.__isub__(other)
def __isub__(self, other):
if self._pending_removals:
self._commit_removals()
if self is other:
self.data.clear()
else:
self.data.difference_update(ref(item) for item in other)
return self
def intersection(self, other):
return self.__class__(item for item in other if item in self)
__and__ = intersection
def intersection_update(self, other):
self.__iand__(other)
def __iand__(self, other):
if self._pending_removals:
self._commit_removals()
self.data.intersection_update(ref(item) for item in other)
return self
def issubset(self, other):
return self.data.issubset(ref(item) for item in other)
__le__ = issubset
def __lt__(self, other):
return self.data < set(ref(item) for item in other)
def issuperset(self, other):
return self.data.issuperset(ref(item) for item in other)
__ge__ = issuperset
def __gt__(self, other):
return self.data > set(ref(item) for item in other)
def __eq__(self, other):
if not isinstance(other, self.__class__):
return NotImplemented
return self.data == set(ref(item) for item in other)
def __ne__(self, other):
opposite = self.__eq__(other)
if opposite is NotImplemented:
return NotImplemented
return not opposite
def symmetric_difference(self, other):
newset = self.copy()
newset.symmetric_difference_update(other)
return newset
__xor__ = symmetric_difference
def symmetric_difference_update(self, other):
self.__ixor__(other)
def __ixor__(self, other):
if self._pending_removals:
self._commit_removals()
if self is other:
self.data.clear()
else:
self.data.symmetric_difference_update(ref(item, self._remove) for item in other)
return self
def union(self, other):
return self.__class__(e for s in (self, other) for e in s)
__or__ = union
def isdisjoint(self, other):
return len(self.intersection(other)) == 0