[Zodb-checkins] CVS: Zope/lib/python/persistent/tests -
test_PickleCache.py:1.2 test_list.py:1.2
test_overriding_attrs.py:1.2 test_persistent.py:1.2
test_wref.py:1.2 testPersistent.py:1.3 test_pickle.py:1.4
Jeremy Hylton
jeremy at zope.com
Wed Feb 18 21:59:33 EST 2004
Update of /cvs-repository/Zope/lib/python/persistent/tests
In directory cvs.zope.org:/tmp/cvs-serv12124/lib/python/persistent/tests
Modified Files:
testPersistent.py test_pickle.py
Added Files:
test_PickleCache.py test_list.py test_overriding_attrs.py
test_persistent.py test_wref.py
Log Message:
Merge zope3-zodb3-devel-branch to the Zope head (Zope 2 head).
Added support for persistent weak references and
PersistentWeakKeyDictionary.
Add _p_invalidate() method to persistence API. _p_deactivate() is
advisory from the cache. _p_invalidate() is called when database
invalidates object.
Port support for getattr/setattr hacks from ZODB4. The doctest tests
describe the new technique pretty well.
Remove unused arguments from some cPickleCache calls.
=== Zope/lib/python/persistent/tests/test_PickleCache.py 1.1 => 1.2 ===
--- /dev/null Wed Feb 18 21:59:33 2004
+++ Zope/lib/python/persistent/tests/test_PickleCache.py Wed Feb 18 21:59:32 2004
@@ -0,0 +1,52 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Unit tests for PickleCache
+
+$Id$
+"""
+
+class DummyConnection:
+
+ def setklassstate(self, obj):
+ """Method used by PickleCache."""
+
+
+def test_delitem():
+ """
+ >>> from persistent import PickleCache
+ >>> conn = DummyConnection()
+ >>> cache = PickleCache(conn)
+ >>> del cache['']
+ Traceback (most recent call last):
+ ...
+ KeyError: ''
+ >>> from persistent import Persistent
+ >>> p = Persistent()
+ >>> p._p_oid = 'foo'
+ >>> p._p_jar = conn
+ >>> cache['foo'] = p
+ >>> del cache['foo']
+
+ """
+
+from doctest import DocTestSuite
+import unittest
+
+def test_suite():
+ return unittest.TestSuite((
+ DocTestSuite(),
+ ))
+
+if __name__ == '__main__':
+ unittest.main()
=== Zope/lib/python/persistent/tests/test_list.py 1.1 => 1.2 ===
--- /dev/null Wed Feb 18 21:59:33 2004
+++ Zope/lib/python/persistent/tests/test_list.py Wed Feb 18 21:59:32 2004
@@ -0,0 +1,229 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Test the list interface to PersistentList
+"""
+
+import unittest
+from persistent.list import PersistentList
+
+l0 = []
+l1 = [0]
+l2 = [0, 1]
+
+class TestPList(unittest.TestCase):
+
+ def testTheWorld(self):
+ # Test constructors
+ u = PersistentList()
+ u0 = PersistentList(l0)
+ u1 = PersistentList(l1)
+ u2 = PersistentList(l2)
+
+ uu = PersistentList(u)
+ uu0 = PersistentList(u0)
+ uu1 = PersistentList(u1)
+ uu2 = PersistentList(u2)
+
+ v = PersistentList(tuple(u))
+ class OtherList:
+ def __init__(self, initlist):
+ self.__data = initlist
+ def __len__(self):
+ return len(self.__data)
+ def __getitem__(self, i):
+ return self.__data[i]
+ v0 = PersistentList(OtherList(u0))
+ vv = PersistentList("this is also a sequence")
+
+ # Test __repr__
+ eq = self.assertEqual
+
+ eq(str(u0), str(l0), "str(u0) == str(l0)")
+ eq(repr(u1), repr(l1), "repr(u1) == repr(l1)")
+ eq(`u2`, `l2`, "`u2` == `l2`")
+
+ # Test __cmp__ and __len__
+
+ def mycmp(a, b):
+ r = cmp(a, b)
+ if r < 0: return -1
+ if r > 0: return 1
+ return r
+
+ all = [l0, l1, l2, u, u0, u1, u2, uu, uu0, uu1, uu2]
+ for a in all:
+ for b in all:
+ eq(mycmp(a, b), mycmp(len(a), len(b)),
+ "mycmp(a, b) == mycmp(len(a), len(b))")
+
+ # Test __getitem__
+
+ for i in range(len(u2)):
+ eq(u2[i], i, "u2[i] == i")
+
+ # Test __setitem__
+
+ uu2[0] = 0
+ uu2[1] = 100
+ try:
+ uu2[2] = 200
+ except IndexError:
+ pass
+ else:
+ raise TestFailed("uu2[2] shouldn't be assignable")
+
+ # Test __delitem__
+
+ del uu2[1]
+ del uu2[0]
+ try:
+ del uu2[0]
+ except IndexError:
+ pass
+ else:
+ raise TestFailed("uu2[0] shouldn't be deletable")
+
+ # Test __getslice__
+
+ for i in range(-3, 4):
+ eq(u2[:i], l2[:i], "u2[:i] == l2[:i]")
+ eq(u2[i:], l2[i:], "u2[i:] == l2[i:]")
+ for j in range(-3, 4):
+ eq(u2[i:j], l2[i:j], "u2[i:j] == l2[i:j]")
+
+ # Test __setslice__
+
+ for i in range(-3, 4):
+ u2[:i] = l2[:i]
+ eq(u2, l2, "u2 == l2")
+ u2[i:] = l2[i:]
+ eq(u2, l2, "u2 == l2")
+ for j in range(-3, 4):
+ u2[i:j] = l2[i:j]
+ eq(u2, l2, "u2 == l2")
+
+ uu2 = u2[:]
+ uu2[:0] = [-2, -1]
+ eq(uu2, [-2, -1, 0, 1], "uu2 == [-2, -1, 0, 1]")
+ uu2[0:] = []
+ eq(uu2, [], "uu2 == []")
+
+ # Test __contains__
+ for i in u2:
+ self.failUnless(i in u2, "i in u2")
+ for i in min(u2)-1, max(u2)+1:
+ self.failUnless(i not in u2, "i not in u2")
+
+ # Test __delslice__
+
+ uu2 = u2[:]
+ del uu2[1:2]
+ del uu2[0:1]
+ eq(uu2, [], "uu2 == []")
+
+ uu2 = u2[:]
+ del uu2[1:]
+ del uu2[:1]
+ eq(uu2, [], "uu2 == []")
+
+ # Test __add__, __radd__, __mul__ and __rmul__
+
+ #self.failUnless(u1 + [] == [] + u1 == u1, "u1 + [] == [] + u1 == u1")
+ self.failUnless(u1 + [1] == u2, "u1 + [1] == u2")
+ #self.failUnless([-1] + u1 == [-1, 0], "[-1] + u1 == [-1, 0]")
+ self.failUnless(u2 == u2*1 == 1*u2, "u2 == u2*1 == 1*u2")
+ self.failUnless(u2+u2 == u2*2 == 2*u2, "u2+u2 == u2*2 == 2*u2")
+ self.failUnless(u2+u2+u2 == u2*3 == 3*u2, "u2+u2+u2 == u2*3 == 3*u2")
+
+ # Test append
+
+ u = u1[:]
+ u.append(1)
+ eq(u, u2, "u == u2")
+
+ # Test insert
+
+ u = u2[:]
+ u.insert(0, -1)
+ eq(u, [-1, 0, 1], "u == [-1, 0, 1]")
+
+ # Test pop
+
+ u = PersistentList([0, -1, 1])
+ u.pop()
+ eq(u, [0, -1], "u == [0, -1]")
+ u.pop(0)
+ eq(u, [-1], "u == [-1]")
+
+ # Test remove
+
+ u = u2[:]
+ u.remove(1)
+ eq(u, u1, "u == u1")
+
+ # Test count
+ u = u2*3
+ eq(u.count(0), 3, "u.count(0) == 3")
+ eq(u.count(1), 3, "u.count(1) == 3")
+ eq(u.count(2), 0, "u.count(2) == 0")
+
+
+ # Test index
+
+ eq(u2.index(0), 0, "u2.index(0) == 0")
+ eq(u2.index(1), 1, "u2.index(1) == 1")
+ try:
+ u2.index(2)
+ except ValueError:
+ pass
+ else:
+ raise TestFailed("expected ValueError")
+
+ # Test reverse
+
+ u = u2[:]
+ u.reverse()
+ eq(u, [1, 0], "u == [1, 0]")
+ u.reverse()
+ eq(u, u2, "u == u2")
+
+ # Test sort
+
+ u = PersistentList([1, 0])
+ u.sort()
+ eq(u, u2, "u == u2")
+
+ # Test extend
+
+ u = u1[:]
+ u.extend(u2)
+ eq(u, u1 + u2, "u == u1 + u2")
+
+ # Test iadd
+ u = u1[:]
+ u += u2
+ eq(u, u1 + u2, "u == u1 + u2")
+
+ # Test imul
+ u = u1[:]
+ u *= 3
+ eq(u, u1 + u1 + u1, "u == u1 + u1 + u1")
+
+
+def test_suite():
+ return unittest.makeSuite(TestPList)
+
+if __name__ == "__main__":
+ loader = unittest.TestLoader()
+ unittest.main(testLoader=loader)
=== Zope/lib/python/persistent/tests/test_overriding_attrs.py 1.1 => 1.2 ===
--- /dev/null Wed Feb 18 21:59:33 2004
+++ Zope/lib/python/persistent/tests/test_overriding_attrs.py Wed Feb 18 21:59:32 2004
@@ -0,0 +1,401 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Overriding attr methods
+
+This module tests and documents, through example, overriding attribute
+access methods.
+
+$Id$
+"""
+
+from persistent import Persistent
+try:
+ from transaction import get_transaction
+except ImportError:
+ pass # else assume ZODB will install it as a builtin
+from ZODB.tests.util import DB
+
+class SampleOverridingGetattr(Persistent):
+ """Example of overriding __getattr__
+ """
+
+ def __getattr__(self, name):
+ """Get attributes that can't be gotten the usual way
+
+ The __getattr__ method works pretty much the same for persistent
+ classes as it does for other classes. No special handling is
+ needed. If an object is a ghost, then it will be activated before
+ __getattr__ is called.
+
+ In this example, our objects returns a tuple with the attribute
+ name, converted to upper case and the value of _p_changed, for any
+ attribute that isn't handled by the default machinery.
+
+ >>> o = SampleOverridingGetattr()
+ >>> o._p_changed
+ 0
+ >>> o._p_oid
+ >>> o._p_jar
+ >>> o.spam
+ ('SPAM', 0)
+ >>> o.spam = 1
+ >>> o.spam
+ 1
+
+ We'll save the object, so it can be deactivated:
+
+ >>> db = DB()
+ >>> conn = db.open()
+ >>> conn.root()['o'] = o
+ >>> get_transaction().commit()
+ >>> o._p_deactivate()
+ >>> o._p_changed
+
+ And now, if we ask for an attribute it doesn't have,
+
+ >>> o.eggs
+ ('EGGS', 0)
+
+ And we see that the object was activated before calling the
+ __getattr__ method.
+
+ We always close databases after we use them:
+
+ >>> db.close()
+ """
+ return name.upper(), self._p_changed
+
+class SampleOverridingGetattributeSetattrAndDelattr(Persistent):
+ """Example of overriding __getattribute__, __setattr__, and __delattr__
+
+ In this example, we'll provide an example that shows how to
+ override the __getattribute__, __setattr__, and __delattr__
+ methods. We'll create a class that stores it's attributes in a
+ secret dictionary within it's instance dictionary.
+
+ The class will have the policy that variables with names starting
+ with 'tmp_' will be volatile.
+
+ """
+
+ def __init__(self, **kw):
+ self.__dict__['__secret__'] = kw.copy()
+
+ def __getattribute__(self, name):
+ """Get an attribute value
+
+ The __getattribute__ method is called for all attribute
+ accesses. It overrides the attribute access support inherited
+ from Persistent.
+
+ Our sample class let's us provide initial values as keyword
+ arguments to the constructor:
+
+ >>> o = SampleOverridingGetattributeSetattrAndDelattr(x=1)
+ >>> o._p_changed
+ 0
+ >>> o._p_oid
+ >>> o._p_jar
+ >>> o.x
+ 1
+ >>> o.y
+ Traceback (most recent call last):
+ ...
+ AttributeError: y
+
+ Next, we'll save the object in a database so that we can
+ deactivate it:
+
+ >>> db = DB()
+ >>> conn = db.open()
+ >>> conn.root()['o'] = o
+ >>> get_transaction().commit()
+ >>> o._p_deactivate()
+ >>> o._p_changed
+
+ And we'll get some data:
+
+ >>> o.x
+ 1
+
+ which activates the object:
+
+ >>> o._p_changed
+ 0
+
+ It works for missing attribes too:
+
+ >>> o._p_deactivate()
+ >>> o._p_changed
+
+ >>> o.y
+ Traceback (most recent call last):
+ ...
+ AttributeError: y
+
+ >>> o._p_changed
+ 0
+
+ See the very important note in the comment below!
+
+ We always close databases after we use them:
+
+ >>> db.close()
+ """
+
+ #################################################################
+ # IMPORTANT! READ THIS! 8->
+ #
+ # We *always* give Persistent a chance first.
+ # Persistent handles certain special attributes, like _p_
+ # attributes. In particular, the base class handles __dict__
+ # and __class__.
+ #
+ # We call _p_getattr. If it returns True, then we have to
+ # use Persistent.__getattribute__ to get the value.
+ #
+ #################################################################
+ if Persistent._p_getattr(self, name):
+ return Persistent.__getattribute__(self, name)
+
+ # Data should be in our secret dictionary:
+ secret = self.__dict__['__secret__']
+ if name in secret:
+ return secret[name]
+
+ # Maybe it's a method:
+ meth = getattr(self.__class__, name, None)
+ if meth is None:
+ raise AttributeError, name
+
+ return meth.__get__(self, self.__class__)
+
+
+ def __setattr__(self, name, value):
+ """Set an attribute value
+
+ The __setattr__ method is called for all attribute
+ assignments. It overrides the attribute assignment support
+ inherited from Persistent.
+
+ Implementors of __setattr__ methods:
+
+ 1. Must call Persistent._p_setattr first to allow it
+ to handle some attributes and to make sure that the object
+ is activated if necessary, and
+
+ 2. Must set _p_changed to mark objects as changed.
+
+ See the comments in the source below.
+
+ >>> o = SampleOverridingGetattributeSetattrAndDelattr()
+ >>> o._p_changed
+ 0
+ >>> o._p_oid
+ >>> o._p_jar
+ >>> o.x
+ Traceback (most recent call last):
+ ...
+ AttributeError: x
+
+ >>> o.x = 1
+ >>> o.x
+ 1
+
+ Because the implementation doesn't store attributes directly
+ in the instance dictionary, we don't have a key for the attribute:
+
+ >>> 'x' in o.__dict__
+ False
+
+ Next, we'll save the object in a database so that we can
+ deactivate it:
+
+ >>> db = DB()
+ >>> conn = db.open()
+ >>> conn.root()['o'] = o
+ >>> get_transaction().commit()
+ >>> o._p_deactivate()
+ >>> o._p_changed
+
+ We'll modify an attribute
+
+ >>> o.y = 2
+ >>> o.y
+ 2
+
+ which reactivates it, and markes it as modified, because our
+ implementation marked it as modified:
+
+ >>> o._p_changed
+ 1
+
+ Now, if commit:
+
+ >>> get_transaction().commit()
+ >>> o._p_changed
+ 0
+
+ And deactivate the object:
+
+ >>> o._p_deactivate()
+ >>> o._p_changed
+
+ and then set a variable with a name starting with 'tmp_',
+ The object will be activated, but not marked as modified,
+ because our __setattr__ implementation doesn't mark the
+ object as changed if the name starts with 'tmp_':
+
+ >>> o.tmp_foo = 3
+ >>> o._p_changed
+ 0
+ >>> o.tmp_foo
+ 3
+
+ We always close databases after we use them:
+
+ >>> db.close()
+
+ """
+
+ #################################################################
+ # IMPORTANT! READ THIS! 8->
+ #
+ # We *always* give Persistent a chance first.
+ # Persistent handles certain special attributes, like _p_
+ # attributes.
+ #
+ # We call _p_setattr. If it returns True, then we are done.
+ # It has already set the attribute.
+ #
+ #################################################################
+ if Persistent._p_setattr(self, name, value):
+ return
+
+ self.__dict__['__secret__'][name] = value
+
+ if not name.startswith('tmp_'):
+ self._p_changed = 1
+
+ def __delattr__(self, name):
+ """Delete an attribute value
+
+ The __delattr__ method is called for all attribute
+ deletions. It overrides the attribute deletion support
+ inherited from Persistent.
+
+ Implementors of __delattr__ methods:
+
+ 1. Must call Persistent._p_delattr first to allow it
+ to handle some attributes and to make sure that the object
+ is activated if necessary, and
+
+ 2. Must set _p_changed to mark objects as changed.
+
+ See the comments in the source below.
+
+ >>> o = SampleOverridingGetattributeSetattrAndDelattr(
+ ... x=1, y=2, tmp_z=3)
+ >>> o._p_changed
+ 0
+ >>> o._p_oid
+ >>> o._p_jar
+ >>> o.x
+ 1
+ >>> del o.x
+ >>> o.x
+ Traceback (most recent call last):
+ ...
+ AttributeError: x
+
+ Next, we'll save the object in a database so that we can
+ deactivate it:
+
+ >>> db = DB()
+ >>> conn = db.open()
+ >>> conn.root()['o'] = o
+ >>> get_transaction().commit()
+ >>> o._p_deactivate()
+ >>> o._p_changed
+
+ If we delete an attribute:
+
+ >>> del o.y
+
+ The object is activated. It is also marked as changed because
+ our implementation marked it as changed.
+
+ >>> o._p_changed
+ 1
+ >>> o.y
+ Traceback (most recent call last):
+ ...
+ AttributeError: y
+
+ >>> o.tmp_z
+ 3
+
+ Now, if commit:
+
+ >>> get_transaction().commit()
+ >>> o._p_changed
+ 0
+
+ And deactivate the object:
+
+ >>> o._p_deactivate()
+ >>> o._p_changed
+
+ and then delete a variable with a name starting with 'tmp_',
+ The object will be activated, but not marked as modified,
+ because our __delattr__ implementation doesn't mark the
+ object as changed if the name starts with 'tmp_':
+
+ >>> del o.tmp_z
+ >>> o._p_changed
+ 0
+ >>> o.tmp_z
+ Traceback (most recent call last):
+ ...
+ AttributeError: tmp_z
+
+ We always close databases after we use them:
+
+ >>> db.close()
+
+ """
+
+ #################################################################
+ # IMPORTANT! READ THIS! 8->
+ #
+ # We *always* give Persistent a chance first.
+ # Persistent handles certain special attributes, like _p_
+ # attributes.
+ #
+ # We call _p_delattr. If it returns True, then we are done.
+ # It has already deleted the attribute.
+ #
+ #################################################################
+ if Persistent._p_delattr(self, name):
+ return
+
+ del self.__dict__['__secret__'][name]
+
+ if not name.startswith('tmp_'):
+ self._p_changed = 1
+
+
+def test_suite():
+ from doctest import DocTestSuite
+ return DocTestSuite()
=== Zope/lib/python/persistent/tests/test_persistent.py 1.1 => 1.2 ===
--- /dev/null Wed Feb 18 21:59:33 2004
+++ Zope/lib/python/persistent/tests/test_persistent.py Wed Feb 18 21:59:32 2004
@@ -0,0 +1,373 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+import unittest
+
+from persistent import Persistent
+from persistent.interfaces import IPersistent
+
+try:
+ import zope.interface
+except ImportError:
+ interfaces = False
+else:
+ interfaces = True
+
+class Test(unittest.TestCase):
+
+ klass = None # override in subclass
+
+ def testSaved(self):
+ p = self.klass()
+ p._p_oid = '\0\0\0\0\0\0hi'
+ dm = DM()
+ p._p_jar = dm
+ self.assertEqual(p._p_changed, 0)
+ self.assertEqual(dm.called, 0)
+ p.inc()
+ self.assertEqual(p._p_changed, 1)
+ self.assertEqual(dm.called, 1)
+ p.inc()
+ self.assertEqual(p._p_changed, 1)
+ self.assertEqual(dm.called, 1)
+ p._p_deactivate()
+ self.assertEqual(p._p_changed, 1)
+ self.assertEqual(dm.called, 1)
+ p._p_deactivate()
+ self.assertEqual(p._p_changed, 1)
+ self.assertEqual(dm.called, 1)
+ del p._p_changed
+ # XXX deal with current cPersistence implementation
+ if p._p_changed != 3:
+ self.assertEqual(p._p_changed, None)
+ self.assertEqual(dm.called, 1)
+ p.inc()
+ self.assertEqual(p.x, 43)
+ self.assertEqual(p._p_changed, 1)
+ self.assertEqual(dm.called, 2)
+ p._p_changed = 0
+ self.assertEqual(p._p_changed, 0)
+ self.assertEqual(dm.called, 2)
+ self.assertEqual(p.x, 43)
+ p.inc()
+ self.assertEqual(p._p_changed, 1)
+ self.assertEqual(dm.called, 3)
+
+ def testUnsaved(self):
+ p = self.klass()
+
+ self.assertEqual(p.x, 0)
+ self.assertEqual(p._p_changed, 0)
+ self.assertEqual(p._p_jar, None)
+ self.assertEqual(p._p_oid, None)
+ p.inc()
+ p.inc()
+ self.assertEqual(p.x, 2)
+ self.assertEqual(p._p_changed, 0)
+
+ p._p_deactivate()
+ self.assertEqual(p._p_changed, 0)
+ p._p_changed = 1
+ self.assertEqual(p._p_changed, 0)
+ p._p_deactivate()
+ self.assertEqual(p._p_changed, 0)
+ del p._p_changed
+ self.assertEqual(p._p_changed, 0)
+ if self.has_dict:
+ self.failUnless(p.__dict__)
+ self.assertEqual(p.x, 2)
+
+ def testState(self):
+ p = self.klass()
+ self.assertEqual(p.__getstate__(), {'x': 0})
+ self.assertEqual(p._p_changed, 0)
+ p.__setstate__({'x':5})
+ self.assertEqual(p._p_changed, 0)
+ if self.has_dict:
+ p._v_foo = 2
+ self.assertEqual(p.__getstate__(), {'x': 5})
+ self.assertEqual(p._p_changed, 0)
+
+ def testSetStateSerial(self):
+ p = self.klass()
+ p._p_serial = '00000012'
+ p.__setstate__(p.__getstate__())
+ self.assertEqual(p._p_serial, '00000012')
+
+ def testDirectChanged(self):
+ p = self.klass()
+ p._p_oid = 1
+ dm = DM()
+ p._p_jar = dm
+ self.assertEqual(p._p_changed, 0)
+ self.assertEqual(dm.called, 0)
+ p._p_changed = 1
+ self.assertEqual(dm.called, 1)
+
+ def testGhostChanged(self):
+ # An object is a ghost, and it's _p_changed it set to True.
+ # This assignment should have no effect.
+ p = self.klass()
+ p._p_oid = 1
+ dm = DM()
+ p._p_jar = dm
+ p._p_deactivate()
+ self.assertEqual(p._p_changed, None)
+ p._p_changed = True
+ self.assertEqual(p._p_changed, None)
+
+ def testRegistrationFailure(self):
+ p = self.klass()
+ p._p_oid = 1
+ dm = BrokenDM()
+ p._p_jar = dm
+ self.assertEqual(p._p_changed, 0)
+ self.assertEqual(dm.called, 0)
+ try:
+ p._p_changed = 1
+ except NotImplementedError:
+ pass
+ else:
+ raise AssertionError("Exception not propagated")
+ self.assertEqual(dm.called, 1)
+ self.assertEqual(p._p_changed, 0)
+
+ def testLoadFailure(self):
+ p = self.klass()
+ p._p_oid = 1
+ dm = BrokenDM()
+ p._p_jar = dm
+ p._p_deactivate() # make it a ghost
+
+ try:
+ p._p_activate()
+ except NotImplementedError:
+ pass
+ else:
+ raise AssertionError("Exception not propagated")
+ self.assertEqual(p._p_changed, None)
+
+ def testActivate(self):
+ p = self.klass()
+ dm = DM()
+ p._p_oid = 1
+ p._p_jar = dm
+ p._p_changed = 0
+ p._p_deactivate()
+ # XXX does this really test the activate method?
+ p._p_activate()
+ self.assertEqual(p._p_changed, 0)
+ self.assertEqual(p.x, 42)
+
+ def testDeactivate(self):
+ p = self.klass()
+ dm = DM()
+ p._p_oid = 1
+ p._p_deactivate() # this deactive has no effect
+ self.assertEqual(p._p_changed, 0)
+ p._p_jar = dm
+ p._p_changed = 0
+ p._p_deactivate()
+ self.assertEqual(p._p_changed, None)
+ p._p_activate()
+ self.assertEqual(p._p_changed, 0)
+ self.assertEqual(p.x, 42)
+
+ if interfaces:
+ def testInterface(self):
+ self.assert_(IPersistent.isImplementedByInstancesOf(Persistent),
+ "%s does not implement IPersistent" % Persistent)
+ p = Persistent()
+ self.assert_(IPersistent.isImplementedBy(p),
+ "%s does not implement IPersistent" % p)
+
+ self.assert_(IPersistent.isImplementedByInstancesOf(P),
+ "%s does not implement IPersistent" % P)
+ p = self.klass()
+ self.assert_(IPersistent.isImplementedBy(p),
+ "%s does not implement IPersistent" % p)
+
+ def testDataManagerAndAttributes(self):
+ # Test to cover an odd bug where the instance __dict__ was
+ # set at the same location as the data manager in the C type.
+ p = P()
+ p.inc()
+ p.inc()
+ self.assert_('x' in p.__dict__)
+ self.assert_(p._p_jar is None)
+
+ def testMultipleInheritance(self):
+ # make sure it is possible to inherit from two different
+ # subclasses of persistent.
+ class A(Persistent):
+ pass
+ class B(Persistent):
+ pass
+ class C(A, B):
+ pass
+ class D(object):
+ pass
+ class E(D, B):
+ pass
+
+ def testMultipleMeta(self):
+ # make sure it's possible to define persistent classes
+ # with a base whose metaclass is different
+ class alternateMeta(type):
+ pass
+ class alternate(object):
+ __metaclass__ = alternateMeta
+ class mixedMeta(alternateMeta, type):
+ pass
+ class mixed(alternate,Persistent):
+ __metaclass__ = mixedMeta
+
+ def testSlots(self):
+ # Verify that Persistent classes behave the same way
+ # as pure Python objects where '__slots__' and '__dict__'
+ # are concerned.
+
+ class noDict(object):
+ __slots__ = ['foo']
+
+ class shouldHaveDict(noDict):
+ pass
+
+ class p_noDict(Persistent):
+ __slots__ = ['foo']
+
+ class p_shouldHaveDict(p_noDict):
+ pass
+
+ self.assertEqual(noDict.__dictoffset__, 0)
+ self.assertEqual(p_noDict.__dictoffset__, 0)
+
+ self.assert_(shouldHaveDict.__dictoffset__ <> 0)
+ self.assert_(p_shouldHaveDict.__dictoffset__ <> 0)
+
+ def testBasicTypeStructure(self):
+ # test that a persistent class has a sane C type structure
+ # use P (defined below) as simplest example
+ self.assertEqual(Persistent.__dictoffset__, 0)
+ self.assertEqual(Persistent.__weakrefoffset__, 0)
+ self.assert_(Persistent.__basicsize__ > object.__basicsize__)
+ self.assert_(P.__dictoffset__)
+ self.assert_(P.__weakrefoffset__)
+ self.assert_(P.__dictoffset__ < P.__weakrefoffset__)
+ self.assert_(P.__basicsize__ > Persistent.__basicsize__)
+
+ def testDeactivateErrors(self):
+ p = self.klass()
+ p._p_oid = '\0\0\0\0\0\0hi'
+ dm = DM()
+ p._p_jar = dm
+
+ def typeerr(*args, **kwargs):
+ self.assertRaises(TypeError, p, *args, **kwargs)
+
+ typeerr(1)
+ typeerr(1, 2)
+ typeerr(spam=1)
+ typeerr(spam=1, force=1)
+
+ p._p_changed = True
+ class Err(object):
+ def __nonzero__(self):
+ raise RuntimeError
+
+ typeerr(force=Err())
+
+class P(Persistent):
+ def __init__(self):
+ self.x = 0
+ def inc(self):
+ self.x += 1
+
+class P2(P):
+ def __getstate__(self):
+ return 42
+ def __setstate__(self, v):
+ self.v = v
+
+class B(Persistent):
+
+ __slots__ = ["x", "_p_serial"]
+
+ def __init__(self):
+ self.x = 0
+
+ def inc(self):
+ self.x += 1
+
+ def __getstate__(self):
+ return {'x': self.x}
+
+ def __setstate__(self, state):
+ self.x = state['x']
+
+class DM:
+ def __init__(self):
+ self.called = 0
+ def register(self, ob):
+ self.called += 1
+ def setstate(self, ob):
+ ob.__setstate__({'x': 42})
+
+class BrokenDM(DM):
+
+ def register(self,ob):
+ self.called += 1
+ raise NotImplementedError
+
+ def setstate(self,ob):
+ raise NotImplementedError
+
+class PersistentTest(Test):
+ klass = P
+ has_dict = 1
+
+ def testPicklable(self):
+ import pickle
+
+ p = self.klass()
+ p.inc()
+ p2 = pickle.loads(pickle.dumps(p))
+ self.assertEqual(p2.__class__, self.klass)
+
+ # verify that the inc is reflected:
+ self.assertEqual(p2.x, p.x)
+
+ # This assertion would be invalid. Interfaces
+ # are compared by identity and copying doesn't
+ # preserve identity. We would get false negatives due
+ # to the differing identities of the original and copied
+ # PersistentInterface:
+ # self.assertEqual(p2.__dict__, p.__dict__)
+
+ def testPicklableWCustomState(self):
+ import pickle
+
+ p = P2()
+ p2 = pickle.loads(pickle.dumps(p))
+ self.assertEqual(p2.__class__, P2);
+ self.assertEqual(p2.__dict__, {'v': 42})
+
+class BasePersistentTest(Test):
+ klass = B
+ has_dict = 0
+
+def test_suite():
+ s = unittest.TestSuite()
+ for klass in PersistentTest, BasePersistentTest:
+ s.addTest(unittest.makeSuite(klass))
+ return s
=== Zope/lib/python/persistent/tests/test_wref.py 1.1 => 1.2 ===
--- /dev/null Wed Feb 18 21:59:33 2004
+++ Zope/lib/python/persistent/tests/test_wref.py Wed Feb 18 21:59:32 2004
@@ -0,0 +1,25 @@
+##############################################################################
+#
+# Copyright (c) 2003 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""XXX short summary goes here.
+
+$Id$
+"""
+import unittest
+from doctest import DocTestSuite
+
+def test_suite():
+ return DocTestSuite('persistent.wref')
+
+if __name__ == '__main__':
+ unittest.main()
=== Zope/lib/python/persistent/tests/testPersistent.py 1.2 => 1.3 ===
--- Zope/lib/python/persistent/tests/testPersistent.py:1.2 Fri Nov 28 11:44:56 2003
+++ Zope/lib/python/persistent/tests/testPersistent.py Wed Feb 18 21:59:32 2004
@@ -15,7 +15,7 @@
import time
import unittest
-from persistent import Persistent
+from persistent import Persistent, GHOST, UPTODATE, CHANGED, STICKY
from persistent.cPickleCache import PickleCache
from persistent.TimeStamp import TimeStamp
from ZODB.utils import p64
@@ -111,7 +111,7 @@
obj._p_jar = 12
self.assertRaises(ValueError, setoid)
- def testChanged(self):
+ def testChangedAndState(self):
obj = P()
self.jar.add(obj)
@@ -122,24 +122,29 @@
obj.x = 1
self.assertEqual(obj._p_changed, 1)
+ self.assertEqual(obj._p_state, CHANGED)
self.assert_(obj in self.jar.registered)
obj._p_changed = 0
self.assertEqual(obj._p_changed, 0)
+ self.assertEqual(obj._p_state, UPTODATE)
self.jar.registered.clear()
obj._p_changed = 1
self.assertEqual(obj._p_changed, 1)
+ self.assertEqual(obj._p_state, CHANGED)
self.assert_(obj in self.jar.registered)
# setting obj._p_changed to None ghostifies if the
# object is in the up-to-date state, but not otherwise.
obj._p_changed = None
self.assertEqual(obj._p_changed, 1)
+ self.assertEqual(obj._p_state, CHANGED)
obj._p_changed = 0
# Now it's a ghost.
obj._p_changed = None
self.assertEqual(obj._p_changed, None)
+ self.assertEqual(obj._p_state, GHOST)
obj = P()
self.jar.add(obj)
@@ -148,6 +153,34 @@
# you delete the _p_changed attribute.
del obj._p_changed
self.assertEqual(obj._p_changed, None)
+ self.assertEqual(obj._p_state, GHOST)
+
+ def testStateReadonly(self):
+ # make sure we can't write to _p_state; we don't want yet
+ # another way to change state!
+ obj = P()
+ def setstate(value):
+ obj._p_state = value
+ self.assertRaises(TypeError, setstate, GHOST)
+ self.assertRaises(TypeError, setstate, UPTODATE)
+ self.assertRaises(TypeError, setstate, CHANGED)
+ self.assertRaises(TypeError, setstate, STICKY)
+
+ def testInvalidate(self):
+ obj = P()
+ self.jar.add(obj)
+
+ self.assertEqual(obj._p_changed, 0)
+ self.assertEqual(obj._p_state, UPTODATE)
+ obj._p_invalidate()
+ self.assertEqual(obj._p_changed, None)
+ self.assertEqual(obj._p_state, GHOST)
+
+ obj._p_activate()
+ obj.x = 1
+ obj._p_invalidate()
+ self.assertEqual(obj._p_changed, None)
+ self.assertEqual(obj._p_state, GHOST)
def testSerial(self):
noserial = "\000" * 8
@@ -196,6 +229,7 @@
# The getattr hook modified the object, so it should now be
# in the changed state.
self.assertEqual(obj._p_changed, 1)
+ self.assertEqual(obj._p_state, CHANGED)
self.assertEqual(obj.curly, 2)
self.assertEqual(obj.moe, 3)
@@ -213,6 +247,7 @@
# The getattr hook modified the object, so it should now be
# in the changed state.
self.assertEqual(obj._p_changed, 1)
+ self.assertEqual(obj._p_state, CHANGED)
self.assertEqual(obj.curly, 2)
self.assertEqual(obj.moe, 3)
=== Zope/lib/python/persistent/tests/test_pickle.py 1.3 => 1.4 ===
--- Zope/lib/python/persistent/tests/test_pickle.py:1.3 Mon Dec 29 17:40:50 2003
+++ Zope/lib/python/persistent/tests/test_pickle.py Wed Feb 18 21:59:32 2004
@@ -73,7 +73,8 @@
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
- >>> pickle.loads(pickle.dumps(x, 2)) == x
+
+ XXX disable until Python 2.3.4 >>> pickle.loads(pickle.dumps(x, 2)) == x
1
>>> x.__setstate__({'z': 1})
@@ -123,7 +124,8 @@
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
- >>> pickle.loads(pickle.dumps(x, 2)) == x
+
+ XXX disable until Python 2.3.4 >>> pickle.loads(pickle.dumps(x, 2)) == x
1
"""
@@ -164,7 +166,8 @@
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
- >>> pickle.loads(pickle.dumps(x, 2)) == x
+
+ XXX disable until Python 2.3.4 >>> pickle.loads(pickle.dumps(x, 2)) == x
1
>>> x.s4 = 'spam'
@@ -180,7 +183,8 @@
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
- >>> pickle.loads(pickle.dumps(x, 2)) == x
+
+ XXX disable until Python 2.3.4 >>> pickle.loads(pickle.dumps(x, 2)) == x
1
"""
@@ -217,7 +221,8 @@
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
- >>> pickle.loads(pickle.dumps(x, 2)) == x
+
+ XXX disable until Python 2.3.4 >>> pickle.loads(pickle.dumps(x, 2)) == x
1
>>> x.s4 = 'spam'
@@ -234,7 +239,8 @@
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
- >>> pickle.loads(pickle.dumps(x, 2)) == x
+
+ XXX disable until Python 2.3.4 >>> pickle.loads(pickle.dumps(x, 2)) == x
1
"""
@@ -258,7 +264,8 @@
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
- >>> pickle.loads(pickle.dumps(x, 2)) == x
+
+ XXX disable until Python 2.3.4 >>> pickle.loads(pickle.dumps(x, 2)) == x
1
>>> x.s4 = 'spam'
@@ -275,7 +282,8 @@
1
>>> pickle.loads(pickle.dumps(x, 1)) == x
1
- >>> pickle.loads(pickle.dumps(x, 2)) == x
+
+ XXX disable until Python 2.3.4 >>> pickle.loads(pickle.dumps(x, 2)) == x
1
"""
More information about the Zodb-checkins
mailing list