[Checkins] SVN: zc.set/trunk/src/zc/set/ Initial checkin.
Gary Poster
gary at zope.com
Tue Aug 15 16:30:25 EDT 2006
Log message for revision 69533:
Initial checkin.
Changed:
A zc.set/trunk/src/zc/set/README.txt
A zc.set/trunk/src/zc/set/__init__.py
A zc.set/trunk/src/zc/set/tests.py
-=-
Added: zc.set/trunk/src/zc/set/README.txt
===================================================================
--- zc.set/trunk/src/zc/set/README.txt 2006-08-15 20:26:46 UTC (rev 69532)
+++ zc.set/trunk/src/zc/set/README.txt 2006-08-15 20:30:24 UTC (rev 69533)
@@ -0,0 +1,384 @@
+Persistent sets are persistent objects that have the API of standard
+Python sets. The persistent set should work the same as normal sets,
+except that changes to them are persistent.
+
+They have the same limitation as persistent lists and persistent
+mappings, as found in the `persistent` package: unlike `BTree` package
+data structures, changes copy the entire object in the database. This
+generally means that persistent sets, like persistent lists and
+persistent mappings, are inappropriate for very large collections. For
+those, use `BTree` data structures.
+
+The rest of this file is tests, not documentation. Find out about the
+Python set API from standard Python documentation
+(http://docs.python.org/lib/types-set.html, for instance) and find out about
+persistence in the ZODB documentation
+(http://www.zope.org/Wikis/ZODB/FrontPage/guide/index.html, for instance).
+
+The persistent set module contains a simple persistent version of a set, that
+inherits from persistent.Persistent and marks _p_changed = True for any
+potentially mutating operation.
+
+ >>> from ZODB.tests.util import DB
+ >>> db = DB()
+ >>> conn = db.open()
+ >>> root = conn.root()
+ >>> import zope.app.folder # import rootFolder
+ >>> app = root['Application'] = zope.app.folder.rootFolder()
+ >>> import transaction
+ >>> transaction.commit()
+
+ >>> from zc.set import Set
+ >>> s = Set()
+ >>> app['s'] = s
+ >>> transaction.commit()
+
+ >>> import persistent.interfaces
+ >>> persistent.interfaces.IPersistent.providedBy(s)
+ True
+ >>> original = factory() # set in one test run; a persistent set in another
+ >>> sorted(set(dir(original)) - set(dir(s)))
+ []
+
+add sets _p_changed
+
+ >>> s._p_changed = False
+ >>> s.add(1) # add
+ >>> s._p_changed
+ True
+
+__repr__ includes module, class, and a contents view like a normal set
+ >>> s # __repr__
+ zc.set.Set([1])
+
+update works as normal, but sets _p_changed
+
+ >>> s._p_changed = False
+ >>> s.update((2,3,4,5,6,7)) # update
+ >>> s._p_changed
+ True
+
+__iter__ works
+
+ >>> sorted(s) # __iter__
+ [1, 2, 3, 4, 5, 6, 7]
+
+__len__ works
+
+ >>> len(s)
+ 7
+
+as does __contains__
+
+ >>> 3 in s
+ True
+ >>> 'kumquat' in s
+ False
+
+__gt__, __ge__, __eq__, __ne__, __lt__, and __le__ work normally,
+equating with normal set, at least if spelled in the right direction.
+
+ >>> s > original
+ True
+ >>> s >= original
+ True
+ >>> s < original
+ False
+ >>> s <= original
+ False
+ >>> s == original
+ False
+ >>> s != original
+ True
+
+ >>> original.update(s)
+ >>> s > original
+ False
+ >>> s >= original
+ True
+ >>> s < original
+ False
+ >>> s <= original
+ True
+ >>> s == original
+ True
+ >>> s != original
+ False
+
+ >>> original.add(8)
+ >>> s > original
+ False
+ >>> s >= original
+ False
+ >>> s < original
+ True
+ >>> s <= original
+ True
+ >>> s == original
+ False
+ >>> s != original
+ True
+
+I don't know what __cmp__ is supposed to do--it doesn't work with sets--so
+I won't test it.
+
+issubset and issuperset work when it is a subset.
+
+ >>> s.issubset(original)
+ True
+ >>> s.issuperset(original)
+ False
+
+__ior__ works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> s |= original
+ >>> s._p_changed
+ True
+ >>> s == original
+ True
+
+issubset and issuperset work when sets are equal.
+
+ >>> s.issubset(original)
+ True
+ >>> s.issuperset(original)
+ True
+
+issubset and issuperset work when it is a superset.
+
+ >>> s.add(9)
+ >>> s.issubset(original)
+ False
+ >>> s.issuperset(original)
+ True
+
+__hash__ works, insofar as raising an error as it is supposed to.
+
+ >>> hash(original)
+ Traceback (most recent call last):
+ ...
+ TypeError: set objects are unhashable
+
+__iand__ works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> s &= original
+ >>> s._p_changed
+ True
+ >>> sorted(s)
+ [1, 2, 3, 4, 5, 6, 7, 8]
+
+__isub__ works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> s -= factory((1, 2, 3, 4, 5, 6, 7))
+ >>> s._p_changed
+ True
+ >>> sorted(s)
+ [8]
+
+__ixor__ works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> s ^= original
+ >>> s._p_changed
+ True
+ >>> sorted(s)
+ [1, 2, 3, 4, 5, 6, 7]
+
+difference_update works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> s.difference_update((7, 8))
+ >>> s._p_changed
+ True
+ >>> sorted(s)
+ [1, 2, 3, 4, 5, 6]
+
+intersection_update works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> s.intersection_update((2, 3, 4, 5, 6, 7))
+ >>> s._p_changed
+ True
+ >>> sorted(s)
+ [2, 3, 4, 5, 6]
+
+symmetric_difference_update works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> original.add(9)
+ >>> s.symmetric_difference_update(original)
+ >>> s._p_changed
+ True
+ >>> sorted(s)
+ [1, 7, 8, 9]
+
+remove works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> s.remove(1)
+ >>> s._p_changed
+ True
+ >>> sorted(s)
+ [7, 8, 9]
+
+If it raises an error, _p_changed is not set.
+
+ >>> s._p_changed = False
+ >>> s.remove(1)
+ Traceback (most recent call last):
+ ...
+ KeyError: 1
+ >>> s._p_changed
+ False
+ >>> sorted(s)
+ [7, 8, 9]
+
+discard works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> s.discard(9)
+ >>> s._p_changed
+ True
+ >>> sorted(s)
+ [7, 8]
+
+If you discard something that wasn't in the set, _p_changed will still
+be set. This is an efficiency decision, rather than our desired behavior,
+necessarily.
+
+ >>> s._p_changed = False
+ >>> s.discard(9)
+ >>> s._p_changed
+ True
+ >>> sorted(s)
+ [7, 8]
+
+pop works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> s.pop() in (7, 8)
+ True
+ >>> s._p_changed
+ True
+ >>> len(s)
+ 1
+
+clear works, including setting _p_changed
+
+ >>> s._p_changed = False
+ >>> s.clear()
+ >>> s._p_changed
+ True
+ >>> len(s)
+ 0
+
+The methods that return sets all return persistent sets. They otherwise
+work identically.
+
+__and__
+
+ >>> s.update((0,1,2,3,4))
+ >>> res = s & original
+ >>> sorted(res)
+ [1, 2, 3, 4]
+ >>> res.__class__ is s.__class__
+ True
+
+__or__
+
+ >>> res = s | original
+ >>> sorted(res)
+ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+ >>> res.__class__ is s.__class__
+ True
+
+__sub__
+
+ >>> res = s - original
+ >>> sorted(res)
+ [0]
+ >>> res.__class__ is s.__class__
+ True
+
+__xor__
+
+ >>> res = s ^ original
+ >>> sorted(res)
+ [0, 5, 6, 7, 8, 9]
+ >>> res.__class__ is s.__class__
+ True
+
+__rand__
+
+ >>> res = set((3,4,5)) & s
+ >>> sorted(res)
+ [3, 4]
+ >>> res.__class__ is s.__class__
+ True
+
+__ror__
+
+ >>> res = set((3,4,5)) | s
+ >>> sorted(res)
+ [0, 1, 2, 3, 4, 5]
+ >>> res.__class__ is s.__class__
+ True
+
+__rsub__
+
+ >>> res = set((3,4,5)) - s
+ >>> sorted(res)
+ [5]
+ >>> res.__class__ is s.__class__
+ True
+
+__rxor__
+
+ >>> res = set((3,4,5)) ^ s
+ >>> sorted(res)
+ [0, 1, 2, 5]
+ >>> res.__class__ is s.__class__
+ True
+
+difference
+
+ >>> res = s.difference((3,4,5))
+ >>> sorted(res)
+ [0, 1, 2]
+ >>> res.__class__ is s.__class__
+ True
+
+intersection
+
+ >>> res = s.intersection((3,4,5))
+ >>> sorted(res)
+ [3, 4]
+ >>> res.__class__ is s.__class__
+ True
+
+symmetric_difference
+
+ >>> res = s.symmetric_difference((3,4,5))
+ >>> sorted(res)
+ [0, 1, 2, 5]
+ >>> res.__class__ is s.__class__
+ True
+
+union
+
+ >>> res = s.union((3,4,5))
+ >>> sorted(res)
+ [0, 1, 2, 3, 4, 5]
+ >>> res.__class__ is s.__class__
+ True
+
+copy returns...a copy.
+
+ >>> res = s.copy()
+ >>> res == s
+ True
+ >>> res.__class__ is s.__class__
+ True
Property changes on: zc.set/trunk/src/zc/set/README.txt
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.set/trunk/src/zc/set/__init__.py
===================================================================
--- zc.set/trunk/src/zc/set/__init__.py 2006-08-15 20:26:46 UTC (rev 69532)
+++ zc.set/trunk/src/zc/set/__init__.py 2006-08-15 20:30:24 UTC (rev 69533)
@@ -0,0 +1,84 @@
+import persistent
+
+def simpleWrapper(name):
+ def wrapper(self, *args, **kwargs):
+ return getattr(self._data, name)(*args, **kwargs)
+ return wrapper
+
+def mutatingWrapper(name):
+ def wrapper(self, *args, **kwargs):
+ res = getattr(self._data, name)(*args, **kwargs)
+ self._p_changed = True
+ return res
+ return wrapper
+
+def mutatingStrippingWrapper(name):
+ def wrapper(self, other):
+ if isinstance(other, self.__class__):
+ other = other._data
+ getattr(self._data, name)(other)
+ self._p_changed = True
+ return self # this is used necessary for all the __i*__, so we have to
+ # return the mutated value
+ return wrapper
+
+def persistentOutputWrapper(name):
+ def wrapper(self, *args, **kwargs):
+ res = getattr(self._data, name)(*args, **kwargs)
+ inst = self.__class__()
+ inst._data = res
+ return inst
+ return wrapper
+
+def persistentOutputStrippingWrapper(name):
+ def wrapper(self, other):
+ if isinstance(other, self.__class__):
+ other = other._data
+ res = getattr(self._data, name)(other)
+ inst = self.__class__()
+ inst._data = res
+ return inst
+ return wrapper
+
+def strippingWrapper(name):
+ def wrapper(self, other):
+ if isinstance(other, self.__class__):
+ other = other._data
+ return getattr(self._data, name)(other)
+ return wrapper
+
+
+class Set(persistent.Persistent):
+ def __init__(self, iterable=()):
+ self._data = set(iterable)
+
+ for nm in ('__cmp__', '__contains__', '__hash__', '__iter__', '__len__'):
+ locals()[nm] = simpleWrapper(nm)
+
+ for nm in ('__eq__', '__ge__', '__gt__', '__le__', '__lt__', '__ne__',
+ 'issubset', 'issuperset'):
+ locals()[nm] = strippingWrapper(nm)
+
+ for nm in ('difference', 'intersection', 'symmetric_difference', 'union'):
+ locals()[nm] = persistentOutputWrapper(nm)
+
+ for nm in ('__and__', '__or__', '__rand__', '__ror__', '__rsub__',
+ '__rxor__', '__sub__', '__xor__'):
+ locals()[nm] = persistentOutputStrippingWrapper(nm)
+
+ for nm in ('add', 'clear', 'difference_update', 'discard',
+ 'intersection_update', 'pop', 'remove',
+ 'symmetric_difference_update', 'update'):
+ locals()[nm] = mutatingWrapper(nm)
+
+ for nm in ('__iand__', '__ior__', '__isub__', '__ixor__'):
+ locals()[nm] = mutatingStrippingWrapper(nm)
+
+ def copy(self):
+ return self.__class__(self._data)
+
+ def __repr__(self):
+ return '%s.%s%s' % (
+ self.__class__.__module__,
+ self.__class__.__name__,
+ repr(self._data)[3:])
Property changes on: zc.set/trunk/src/zc/set/__init__.py
___________________________________________________________________
Name: svn:eol-style
+ native
Added: zc.set/trunk/src/zc/set/tests.py
===================================================================
--- zc.set/trunk/src/zc/set/tests.py 2006-08-15 20:26:46 UTC (rev 69532)
+++ zc.set/trunk/src/zc/set/tests.py 2006-08-15 20:30:24 UTC (rev 69533)
@@ -0,0 +1,20 @@
+import unittest
+
+from zope.testing import doctest, module
+import zc.set
+
+def setUpSetsOne(test):
+ test.globs['factory'] = set
+
+def setUpSetsTwo(test):
+ test.globs['factory'] = zc.set.Set
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite('README.txt', setUp=setUpSetsOne),
+ doctest.DocFileSuite('README.txt', setUp=setUpSetsTwo),
+ ))
+
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
Property changes on: zc.set/trunk/src/zc/set/tests.py
___________________________________________________________________
Name: svn:eol-style
+ native
More information about the Checkins
mailing list