[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