Re: Re: Trellis + Python 2.6
"Sergey Schetinin" <[email protected]> Thu, 20 Nov 2008 09:42:11 +0200
| Newsgroups | gmane.comp.python.peak |
|---|---|
| Message-ID | <[email protected]> |
Here's an updated patch. Set worked well in my tests, so I'm pretty
confident about it. It had to copy with some changes quite a few lines
from sets.Set, but not too many.
I tried running the test suite but it fails completely:
Traceback (most recent call last):
File "setup.py", line 40, in <module>
py_modules = ['_threading_local'][:sys.version<'2.4']
File "C:\python26\lib\distutils\core.py", line 152, in setup
dist.run_commands()
File "C:\python26\lib\distutils\dist.py", line 975, in run_commands
self.run_command(cmd)
File "C:\python26\lib\distutils\dist.py", line 995, in run_command
cmd_obj.run()
File "build\bdist.win32\egg\setuptools\command\test.py", line 121, in run
File "build\bdist.win32\egg\setuptools\command\test.py", line 101,
in with_project_on_sys_path
File "build\bdist.win32\egg\setuptools\command\test.py", line 135,
in run_tests
File "C:\python26\lib\unittest.py", line 817, in __init__
self.runTests()
File "C:\python26\lib\unittest.py", line 861, in runTests
result = testRunner.run(self.test)
AttributeError: 'NoneType' object has no attribute 'run'
There are a few more changes in this patch that I'd really like to see
in trunk, which I'll explain here:
- 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
InputConflict is pretty useless otherwise. Not sure why it's commented out.
- def wrap(__func, __module):
- """
+ def wrap(__func, __module): pass
+ wrap.__doc__ = """
I requested a few similar changes a few months ago, but it was
declined and for a good reason. I'm asking again about this because I
found out this is the only place I can't monkeypatch on import because
@modifier is used in the module itself. So I can monkeypatch
decorators.rewrap, but not this. The reason I need to distribute apps
with -OO is exactly because it strips docstrings which makes
proprietary code a bit harder to reverse-engineer and because it cuts
distribution size noticeably, by almost 10%. If there's a way to patch
modules before fully imported, that would be good enough for me, but
there's apparently no way to do that.
- raise AssertionError("This should never happen!")
+ raise AssertionError("This should never happen!", self)
Again, pretty useless without knowing the cell.
The rest of changes are for py2.6 compatibility and shouldn't
influence earlier versions at all.
On Wed, Nov 19, 2008 at 05:52, Phillip J. Eby <pje-Wh6+Hckhi6HFNGf7iClzIwC/[email protected]> wrote:
> At 01:05 AM 11/19/2008 +0200, Sergey Schetinin wrote:
>>
>> I'll have to look into using collections.MutableSet, but I won't have
>> any time for this for a few days. Will report when done.
>
> That would require a pretty substantial rewrite, which then wouldn't work
> with any version less than 2.6. (The collections abstract bases don't
> delegate operations to a ._data member.)
_______________________________________________
PEAK mailing list
[email protected]
http://www.eby-sarna.com/mailman/listinfo/peak
py26.patch
(application/octet-stream, 10 KB)
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