cpython: 2cb530243943 Lib/_weakrefset.py (original) (raw)
Mercurial > cpython
view Lib/_weakrefset.py @ 106485:2cb530243943 3.5
Fix #29519: weakref spewing exceptions during interp finalization [#29519]
Ćukasz Langa lukasz@langa.pl | |
---|---|
date | Fri, 10 Feb 2017 00:14:55 -0800 |
parents | a3d86f80c899 |
children |
line wrap: on
line source
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:
# 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:
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):
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))
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()
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 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