Index: stm.py =================================================================== --- stm.py (revision 2595) +++ stm.py (working copy) @@ -1,6 +1,6 @@ """Software Transactional Memory and Observers""" -import weakref, sys, heapq, UserList, UserDict, sets +import weakref, sys, heapq, UserList, UserDict from peak.util.extremes import Max from peak.util import decorators try: Index: trellis.py =================================================================== --- trellis.py (revision 2595) +++ trellis.py (working copy) @@ -1,7 +1,7 @@ from thread import get_ident from weakref import ref from peak.util import addons, decorators -import sys, UserDict, UserList, sets, stm, types, new, weakref, copy +import sys, UserDict, UserList, stm, types, new, weakref, copy from peak.util.extremes import Max from peak.util.symbols import Symbol, NOT_GIVEN @@ -42,12 +42,13 @@ try: set = set except NameError: + import sets set = sets.Set frozenset = sets.ImmutableSet set_like = sets.BaseSet dictlike = dict, sets.BaseSet else: - set_like = set, frozenset, sets.BaseSet + set_like = set, frozenset dictlike = (dict,) + set_like @@ -141,7 +142,7 @@ if value!=self._value: if self._set_by not in (ctrl.current_listener, self): # already set by someone else - raise InputConflict(self._value, value) #self._set_by) #, value, ctrl.current_listener) # XXX + raise InputConflict(self._value, value, self._set_by, ctrl.current_listener) # XXX changed(self) change_attr(self, '_value', value) @@ -292,8 +294,8 @@ a rule, reads performed in the function will not become dependencies of the caller. """ - def wrap(__func, __module): - """ + def wrap(__func, __module): pass + wrap.__doc__ = """ if not __module.ctrl.active: return __module.atomically(__func, $args) elif __module.ctrl.current_listener is None: @@ -506,7 +508,7 @@ return ReadOnlyCell(rule, None, discrete) elif rule is None: return Value(v, discrete) - return ReadOnlyCell.__new__(cls, rule, value, discrete) + return ReadOnlyCell.__new__(cls)#, rule, value, discrete) def _check_const(self): pass # we can never become Constant @@ -529,8 +531,9 @@ super(Cell, self).set_value(value) if self._needs_init: schedule(self) + else: + cancel(self) - value = property(get_value, set_value) def dirty(self): @@ -552,7 +555,7 @@ else: # It should be impossible to get here unless you run the cell # manually. Don't do that. :) - raise AssertionError("This should never happen!") + raise AssertionError("This should never happen!", self) class Effector(SensorBase, Cell): @@ -748,6 +751,8 @@ except KeyError: name = self.__name__ cell = cells.setdefault(name, self.make_cell(typ, ob, name)) + if ctrl.active: + on_undo(cells.pop, name) return cell.value def __repr__(self): @@ -1240,6 +1245,9 @@ strategy is used in each recalcultion that changes the list. If what you really want is e.g. a sorted read-only view on a set, don't use this. """ + if hasattr(UserList.UserList, '__metaclass__'): + class __metaclass__(Component.__metaclass__, UserList.UserList.__metaclass__): + pass updated = todo(lambda self: self.data[:]) future = updated.future @@ -1348,10 +1356,7 @@ - - - -class Set(sets.Set, Component): +class _SetBase(Component): """Mutable set that recalculates observers when it's changed The ``added`` and ``removed`` attributes can be watched for changes, but @@ -1364,28 +1369,6 @@ to_add = _added.future to_remove = _removed.future - def __init__(self, iterable=None, **kw): - """Construct a set from an optional iterable.""" - Component.__init__(self, **kw) - if iterable is not None: - # we can update self._data in place, since no-one has seen it yet - sets.Set._update(self, iterable) - - maintain(make=dict) - def _data(self): - """The dictionary containing the set data.""" - data = self._data - pop = data.pop - if self.removed: - mark_dirty() - for item in self.removed: pop(item, None) - on_undo(data.update, dict.fromkeys(self.removed, True)) - if self.added: - mark_dirty() - data.update(dict.fromkeys(self.added, True)) - for item in self.added: on_undo(pop, item, None) - return data - def __setstate__(self, data): self.__init__(data[0]) @@ -1490,28 +1473,157 @@ else: to_add.add(elt) # Don't got it; add it -def to_dict_or_set(ob): - """Return the most basic set or dict-like object for ob - If ob is a sets.BaseSet, return its ._data; if it's something we can tell - is dictlike, return it as-is. Otherwise, make a dict using .fromkeys() - """ - if isinstance(ob, sets.BaseSet): - return ob._data - elif not isinstance(ob, dictlike): - return dict.fromkeys(ob) - return ob +if sys.version >= '2.6': + from itertools import ifilter, ifilterfalse + MutableSet = getattr(__import__('collections', level=0), 'MutableSet') # absolute import + class Set(_SetBase, MutableSet): + class __metaclass__(_SetBase.__metaclass__, MutableSet.__metaclass__): + pass + def __init__(self, iterable=None, **kw): + """Construct a set from an optional iterable.""" + Component.__init__(self, **kw) + if iterable is not None: + # we can update self._data in place, since no-one has seen it yet + self._data.update(iterable) + __hash__ = None + maintain(make=set) + def _data(self): + """The dictionary containing the set data.""" + data = self._data + if self.removed: + mark_dirty() + data -= self.removed + on_undo(data.update, copy(self.removed)) + if self.added: + mark_dirty() + data.update(self.added) + on_undo(data.difference_update, copy(self.added)) + return data + def __getstate__(self): + return self._data, + def discard(self, item): + try: + self.remove(item) + except KeyError: + pass + def __len__(self): + return len(self._data) + def __iter__(self): + return iter(self._data) + def __contains__(self, item): + return item in self._data + + def update(self, iterable): + self._update(iterable) + + def union(self, other): + result = self.__class__(self._data) + result._data.update(other) + return result + + def intersection(self, other): + other = to_dict_or_set(other) + if len(self) <= len(other): + little, big = self, other + else: + little, big = other, self + common = ifilter(big.__contains__, little) + return self.__class__(common) + + def difference(self, other): + other = to_dict_or_set(other) + return self.__class__(ifilterfalse(other.__contains__, self)) + + def symmetric_difference(self, other): + other = to_dict_or_set(other) + result = self.__class__() + result._data.update(ifilterfalse(other.__contains__, self._data)) + result._data.update(ifilterfalse(self._data.__contains__, other)) + return result + + __or__ = union + __xor__ = symmetric_difference + __and__ = intersection + __sub__ = difference + + issubset = MutableSet.__le__ + issuperset = MutableSet.__ge__ + + def __ixor__(self, other): + """Update a set with the symmetric difference of itself and another.""" + self._binary_sanity_check(other) + self.symmetric_difference_update(other) + return self + + def __isub__(self, other): + """Remove all elements of another set from this set.""" + self._binary_sanity_check(other) + self.difference_update(other) + return self + + + + def to_dict_or_set(ob): + if isinstance(ob, Set): + return ob._data + elif isinstance(ob, dictlike): + return ob + else: + return frozenset(ob) + + + + + +else: + import sets + + class Set(_SetBase, sets.Set): + def __init__(self, iterable=None, **kw): + """Construct a set from an optional iterable.""" + Component.__init__(self, **kw) + if iterable is not None: + # we can update self._data in place, since no-one has seen it yet + sets.Set._update(self, iterable) + + maintain(make=dict) + def _data(self): + """The dictionary containing the set data.""" + data = self._data + pop = data.pop + if self.removed: + mark_dirty() + for item in self.removed: pop(item, None) + on_undo(data.update, dict.fromkeys(self.removed, True)) + if self.added: + mark_dirty() + data.update(dict.fromkeys(self.added, True)) + for item in self.added: on_undo(pop, item, None) + return data + + + def to_dict_or_set(ob): + """Return the most basic set or dict-like object for ob + If ob is a sets.BaseSet, return its ._data; if it's something we can tell + is dictlike, return it as-is. Otherwise, make a dict using .fromkeys() + """ + if isinstance(ob, sets.BaseSet): + return ob._data + elif not isinstance(ob, dictlike): + return dict.fromkeys(ob) + return ob