[Zodb-checkins] CVS: Zope/lib/python/Persistence/tests - __init__.py:1.2 testPersistent.py:1.2 test_ExtensionClass.py:1.2

Jim Fulton cvs-admin at zope.org
Fri Nov 28 11:45:18 EST 2003


Update of /cvs-repository/Zope/lib/python/Persistence/tests
In directory cvs.zope.org:/tmp/cvs-serv3783/lib/python/Persistence/tests

Added Files:
	__init__.py testPersistent.py test_ExtensionClass.py 
Log Message:
Merged Jeremy and Tim's changes from the zodb33-devel-branch.


=== Zope/lib/python/Persistence/tests/__init__.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:45:18 2003
+++ Zope/lib/python/Persistence/tests/__init__.py	Fri Nov 28 11:44:47 2003
@@ -0,0 +1 @@
+# package


=== Zope/lib/python/Persistence/tests/testPersistent.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:45:18 2003
+++ Zope/lib/python/Persistence/tests/testPersistent.py	Fri Nov 28 11:44:47 2003
@@ -0,0 +1,224 @@
+#############################################################################
+#
+# 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.
+#
+##############################################################################
+import pickle
+import time
+import unittest
+
+from Persistence import Persistent
+from persistent.cPickleCache import PickleCache
+from persistent.TimeStamp import TimeStamp
+from ZODB.utils import p64
+
+class Jar(object):
+    """Testing stub for _p_jar attribute."""
+
+    def __init__(self):
+        self.cache = PickleCache(self)
+        self.oid = 1
+        self.registered = {}
+
+    def add(self, obj):
+        obj._p_oid = p64(self.oid)
+        self.oid += 1
+        obj._p_jar = self
+        self.cache[obj._p_oid] = obj
+
+    def close(self):
+        pass
+
+    # the following methods must be implemented to be a jar
+
+    def setklassstate(self):
+        # I don't know what this method does, but the pickle cache
+        # constructor calls it.
+        pass
+
+    def register(self, obj):
+        self.registered[obj] = 1
+
+    def setstate(self, obj):
+        # Trivial setstate() implementation that just re-initializes
+        # the object.  This isn't what setstate() is supposed to do,
+        # but it suffices for the tests.
+        obj.__class__.__init__(obj)
+
+class P(Persistent):
+    pass
+
+class H1(Persistent):
+
+    def __init__(self):
+        self.n = 0
+
+    def __getattr__(self, attr):
+        self.n += 1
+        return self.n
+
+class H2(Persistent):
+
+    def __init__(self):
+        self.n = 0
+
+    def __getattribute__(self, attr):
+        supergetattr = super(H2, self).__getattribute__
+        try:
+            return supergetattr(attr)
+        except AttributeError:
+            n = supergetattr("n")
+            self.n = n + 1
+            return n + 1
+
+class PersistenceTest(unittest.TestCase):
+
+    def setUp(self):
+        self.jar = Jar()
+
+    def tearDown(self):
+        self.jar.close()
+
+    def testOidAndJarAttrs(self):
+        obj = P()
+        self.assertEqual(obj._p_oid, None)
+        obj._p_oid = 12
+        self.assertEqual(obj._p_oid, 12)
+        del obj._p_oid
+
+        self.jar.add(obj)
+
+        # Can't change oid of cache object.
+        def deloid():
+            del obj._p_oid
+        self.assertRaises(ValueError, deloid)
+        def setoid():
+            obj._p_oid = 12
+        self.assertRaises(ValueError, setoid)
+
+        def deloid():
+            del obj._p_jar
+        self.assertRaises(ValueError, deloid)
+        def setoid():
+            obj._p_jar = 12
+        self.assertRaises(ValueError, setoid)
+
+    def testChanged(self):
+        obj = P()
+        self.jar.add(obj)
+
+        # The value returned for _p_changed can be one of:
+        # 0 -- it is not changed
+        # 1 -- it is changed
+        # None -- it is a ghost
+
+        obj.x = 1
+        self.assertEqual(obj._p_changed, 1)
+        self.assert_(obj in self.jar.registered)
+
+        obj._p_changed = 0
+        self.assertEqual(obj._p_changed, 0)
+        self.jar.registered.clear()
+
+        obj._p_changed = 1
+        self.assertEqual(obj._p_changed, 1)
+        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)
+        obj._p_changed = 0
+        # Now it's a ghost.
+        obj._p_changed = None
+        self.assertEqual(obj._p_changed, None)
+
+        obj = P()
+        self.jar.add(obj)
+        obj._p_changed = 1
+        # You can transition directly from modified to ghost if
+        # you delete the _p_changed attribute.
+        del obj._p_changed
+        self.assertEqual(obj._p_changed, None)
+
+    def testSerial(self):
+        noserial = "\000" * 8
+        obj = P()
+        self.assertEqual(obj._p_serial, noserial)
+
+        def set(val):
+            obj._p_serial = val
+        self.assertRaises(ValueError, set, 1)
+        self.assertRaises(ValueError, set, "0123")
+        self.assertRaises(ValueError, set, "012345678")
+        self.assertRaises(ValueError, set, u"01234567")
+
+        obj._p_serial = "01234567"
+        del obj._p_serial
+        self.assertEqual(obj._p_serial, noserial)
+
+    def testMTime(self):
+        obj = P()
+        self.assertEqual(obj._p_mtime, None)
+
+        t = int(time.time())
+        ts = TimeStamp(*time.gmtime(t)[:6])
+        obj._p_serial = repr(ts)
+        self.assertEqual(obj._p_mtime, t)
+        self.assert_(isinstance(obj._p_mtime, float))
+
+    def testPicklable(self):
+        obj = P()
+        obj.attr = "test"
+        s = pickle.dumps(obj)
+        obj2 = pickle.loads(s)
+        self.assertEqual(obj.attr, obj2.attr)
+
+    def testGetattr(self):
+        obj = H1()
+        self.assertEqual(obj.larry, 1)
+        self.assertEqual(obj.curly, 2)
+        self.assertEqual(obj.moe, 3)
+
+        self.jar.add(obj)
+        obj._p_deactivate()
+
+        # The simple Jar used for testing re-initializes the object.
+        self.assertEqual(obj.larry, 1)
+        # The getattr hook modified the object, so it should now be
+        # in the changed state.
+        self.assertEqual(obj._p_changed, 1)
+        self.assertEqual(obj.curly, 2)
+        self.assertEqual(obj.moe, 3)
+
+    def testGetattribute(self):
+        obj = H2()
+        self.assertEqual(obj.larry, 1)
+        self.assertEqual(obj.curly, 2)
+        self.assertEqual(obj.moe, 3)
+
+        self.jar.add(obj)
+        obj._p_deactivate()
+
+        # The simple Jar used for testing re-initializes the object.
+        self.assertEqual(obj.larry, 1)
+        # The getattr hook modified the object, so it should now be
+        # in the changed state.
+        self.assertEqual(obj._p_changed, 1)
+        self.assertEqual(obj.curly, 2)
+        self.assertEqual(obj.moe, 3)
+
+    # XXX Need to decide how __setattr__ and __delattr__ should work,
+    # then write tests.
+
+
+def test_suite():
+    return unittest.makeSuite(PersistenceTest)


=== Zope/lib/python/Persistence/tests/test_ExtensionClass.py 1.1 => 1.2 ===
--- /dev/null	Fri Nov 28 11:45:18 2003
+++ Zope/lib/python/Persistence/tests/test_ExtensionClass.py	Fri Nov 28 11:44:47 2003
@@ -0,0 +1,525 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Test ExtensionClass support in Persistence.Persistent
+
+$Id$
+"""
+
+from Persistence import Persistent
+import pickle
+
+try:
+    import Persistent._Persistence
+except ImportError:
+    pass
+else:
+
+    def test_basic():
+        """
+
+        >>> from ExtensionClass import Base
+
+        - Support for a class initialiser:
+
+          >>> class C(Persistent):
+          ...   def __class_init__(self):
+          ...      print 'class init called'
+          ...      print self.__name__
+          ...   def bar(self):
+          ...      return 'bar called'
+          class init called
+          C
+          >>> c = C()
+          >>> int(c.__class__ is C)
+          1
+          >>> int(c.__class__ is type(c))
+          1
+
+        - Provide an inheritedAttribute method for looking up attributes in
+          base classes:
+
+          >>> class C2(C):
+          ...   def bar(*a):
+          ...      return C2.inheritedAttribute('bar')(*a), 42
+          class init called
+          C2
+          >>> o = C2()
+          >>> o.bar()
+          ('bar called', 42)
+
+          This is for compatability with old code. New code should use super
+          instead.   
+
+        The base class, Base, exists mainly to support the __of__ protocol.
+        The __of__ protocol is similar to __get__ except that __of__ is called
+        when an implementor is retrieved from an instance as well as from a
+        class:
+
+        >>> class O(Base):
+        ...   def __of__(*a):
+        ...      return a
+
+        >>> o1 = O()
+        >>> o2 = O()
+        >>> C.o1 = o1
+        >>> c.o2 = o2
+        >>> c.o1 == (o1, c)
+        1
+        >>> C.o1 == o1
+        1
+        >>> int(c.o2 == (o2, c))
+        1
+
+        We accomplish this by making a class that implements __of__ a
+        descriptor and treating all descriptor ExtensionClasses this way. That
+        is, if an extension class is a descriptor, it's __get__ method will be
+        called even when it is retrieved from an instance.
+
+        >>> class O(Base):
+        ...   def __get__(*a):
+        ...      return a
+        ... 
+        >>> o1 = O()
+        >>> o2 = O()
+        >>> C.o1 = o1
+        >>> c.o2 = o2
+        >>> int(c.o1 == (o1, c, type(c)))
+        1
+        >>> int(C.o1 == (o1, None, type(c)))
+        1
+        >>> int(c.o2 == (o2, c, type(c)))
+        1
+        """
+
+    def test_mixing():
+        """Test working with a classic class
+
+        >>> class Classic: 
+        ...   def x(self): 
+        ...     return 42
+
+        >>> class O(Persistent):
+        ...   def __of__(*a):
+        ...      return a
+
+        >>> class O2(Classic, O):
+        ...   def __of__(*a):
+        ...      return (O2.inheritedAttribute('__of__')(*a), 
+        ...              O2.inheritedAttribute('x')(a[0]))
+
+        >>> class C(Persistent):
+        ...   def __class_init__(self):
+        ...      print 'class init called'
+        ...      print self.__name__
+        ...   def bar(self):
+        ...      return 'bar called'
+        class init called
+        C
+
+        >>> c = C()
+        >>> o2 = O2()
+        >>> c.o2 = o2
+        >>> int(c.o2 == ((o2, c), 42))
+        1
+
+        Test working with a new style
+
+        >>> class Modern(object): 
+        ...   def x(self): 
+        ...     return 42
+
+        >>> class O2(Modern, O):
+        ...   def __of__(*a):
+        ...      return (O2.inheritedAttribute('__of__')(*a), 
+        ...              O2.inheritedAttribute('x')(a[0]))
+
+        >>> o2 = O2()
+        >>> c.o2 = o2
+        >>> int(c.o2 == ((o2, c), 42))
+        1
+
+        """
+
+    def proper_error_on_deleattr():
+        """
+        Florent Guillaume wrote:
+
+        ...
+
+        Excellent.
+        Will it also fix this particularity of ExtensionClass:
+
+
+        >>> class A(Persistent):
+        ...   def foo(self):
+        ...     self.gee
+        ...   def bar(self):
+        ...     del self.gee
+
+        >>> a=A()
+        >>> a.foo()
+        Traceback (most recent call last):
+        ...
+        AttributeError: gee
+
+        >>> a.bar()
+        Traceback (most recent call last):
+        ...
+        AttributeError: 'A' object has no attribute 'gee'
+
+        I.e., the fact that KeyError is raised whereas a normal class would
+        raise AttributeError.
+        """
+
+    def test__basicnew__():
+        """
+        >>> x = Simple.__basicnew__()
+        >>> x.__dict__
+        {}
+        """
+
+    def test_setattr_on_extension_type():
+        """
+        >>> for name in 'x', '_x', 'x_', '__x_y__', '___x__', '__x___', '_x_':
+        ...     setattr(Persistent, name, 1)
+        ...     print getattr(Persistent, name)
+        ...     delattr(Persistent, name)
+        ...     print getattr(Persistent, name, 0)
+        1
+        0
+        1
+        0
+        1
+        0
+        1
+        0
+        1
+        0
+        1
+        0
+        1
+        0
+
+        >>> Persistent.__foo__ = 1
+        Traceback (most recent call last):
+        ...
+        TypeError: can't set attributes of built-in/extension type """ \
+            """'Persistence.Persistent' if the attribute name begins """ \
+            """and ends with __ and contains only 4 _ characters
+
+        >>> Persistent.__foo__
+        Traceback (most recent call last):
+        ...
+        AttributeError: type object 'Persistence.Persistent' """ \
+            """has no attribute '__foo__'
+
+        >>> del Persistent.__foo__
+        Traceback (most recent call last):
+        ...
+        TypeError: can't set attributes of built-in/extension type """ \
+            """'Persistence.Persistent' if the attribute name begins """ \
+            """and ends with __ and contains only 4 _ characters
+
+        """
+
+def test_class_creation_under_stress():
+    """
+    >>> for i in range(100): 
+    ...   class B(Persistent):
+    ...     print i,
+    ...     if i and i%20 == 0:
+    ...         print
+    0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
+    21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
+    41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
+    61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
+    81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
+
+    >>> import gc
+    >>> x = gc.collect()
+
+    """
+
+def print_dict(d):
+    d = d.items()
+    d.sort()
+    print '{%s}' % (', '.join(
+        [('%r: %r' % (k, v)) for (k, v) in d]
+        ))
+
+def cmpattrs(self, other, *attrs):
+    for attr in attrs:
+        if attr[:3] in ('_v_', '_p_'):
+            continue
+        c = cmp(getattr(self, attr, None), getattr(other, attr, None))
+        if c:
+            return c
+    return 0
+
+class Simple(Persistent):
+    def __init__(self, name, **kw):
+        self.__name__ = name
+        self.__dict__.update(kw)
+        self._v_favorite_color = 'blue'
+        self._p_foo = 'bar'
+
+    def __cmp__(self, other):
+        return cmpattrs(self, other, '__class__', *(self.__dict__.keys()))
+
+def test_basic_pickling():
+    """
+    >>> x = Simple('x', aaa=1, bbb='foo')
+
+    >>> x.__getnewargs__()
+    ()
+
+    >>> print_dict(x.__getstate__())
+    {'__name__': 'x', 'aaa': 1, 'bbb': 'foo'}
+    
+    >>> f, (c,), state = x.__reduce__()
+    >>> f.__name__
+    '__newobj__'
+    >>> f.__module__
+    'copy_reg'
+    >>> c.__name__
+    'Simple'
+    
+    >>> print_dict(state)
+    {'__name__': 'x', 'aaa': 1, 'bbb': 'foo'}
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    >>> x.__setstate__({'z': 1})
+    >>> x.__dict__
+    {'z': 1}
+
+    """
+
+class Custom(Simple):
+
+    def __new__(cls, x, y):
+        r = Persistent.__new__(cls)
+        r.x, r.y = x, y
+        return r
+
+    def __init__(self, x, y):
+        self.a = 42
+
+    def __getnewargs__(self):
+        return self.x, self.y
+
+    def __getstate__(self):
+        return self.a
+
+    def __setstate__(self, a):
+        self.a = a
+
+
+def test_pickling_w_overrides():
+    """
+    >>> x = Custom('x', 'y')
+    >>> x.a = 99
+
+    >>> (f, (c, ax, ay), a) = x.__reduce__()
+    >>> f.__name__
+    '__newobj__'
+    >>> f.__module__
+    'copy_reg'
+    >>> c.__name__
+    'Custom'
+    >>> ax, ay, a
+    ('x', 'y', 99)
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+    
+    """
+
+class Slotted(Persistent):
+    __slots__ = 's1', 's2', '_p_splat', '_v_eek'
+    def __init__(self, s1, s2):
+        self.s1, self.s2 = s1, s2
+        self._v_eek = 1
+        self._p_splat = 2
+
+class SubSlotted(Slotted):
+    __slots__ = 's3', 's4'
+    def __init__(self, s1, s2, s3):
+        Slotted.__init__(self, s1, s2)
+        self.s3 = s3
+
+        
+    def __cmp__(self, other):
+        return cmpattrs(self, other, '__class__', 's1', 's2', 's3', 's4')
+
+
+def test_pickling_w_slots_only():
+    """
+    >>> x = SubSlotted('x', 'y', 'z')
+
+    >>> x.__getnewargs__()
+    ()
+
+    >>> d, s = x.__getstate__()
+    >>> d
+    >>> print_dict(s)
+    {'s1': 'x', 's2': 'y', 's3': 'z'}
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    >>> x.s4 = 'spam'
+    
+    >>> d, s = x.__getstate__()
+    >>> d
+    >>> print_dict(s)
+    {'s1': 'x', 's2': 'y', 's3': 'z', 's4': 'spam'}
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    """
+
+class SubSubSlotted(SubSlotted):
+
+    def __init__(self, s1, s2, s3, **kw):
+        SubSlotted.__init__(self, s1, s2, s3)
+        self.__dict__.update(kw)
+        self._v_favorite_color = 'blue'
+        self._p_foo = 'bar'
+        
+    def __cmp__(self, other):
+        return cmpattrs(self, other,
+                        '__class__', 's1', 's2', 's3', 's4',
+                        *(self.__dict__.keys()))
+
+def test_pickling_w_slots():
+    """
+    >>> x = SubSubSlotted('x', 'y', 'z', aaa=1, bbb='foo')
+
+    >>> x.__getnewargs__()
+    ()
+
+    >>> d, s = x.__getstate__()
+    >>> print_dict(d)
+    {'aaa': 1, 'bbb': 'foo'}
+    >>> print_dict(s)
+    {'s1': 'x', 's2': 'y', 's3': 'z'}
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    >>> x.s4 = 'spam'
+    
+    >>> d, s = x.__getstate__()
+    >>> print_dict(d)
+    {'aaa': 1, 'bbb': 'foo'}
+    >>> print_dict(s)
+    {'s1': 'x', 's2': 'y', 's3': 'z', 's4': 'spam'}
+
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    """
+
+def test_pickling_w_slots_w_empty_dict():
+    """
+    >>> x = SubSubSlotted('x', 'y', 'z')
+
+    >>> x.__getnewargs__()
+    ()
+
+    >>> d, s = x.__getstate__()
+    >>> print_dict(d)
+    {}
+    >>> print_dict(s)
+    {'s1': 'x', 's2': 'y', 's3': 'z'}
+    
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    >>> x.s4 = 'spam'
+    
+    >>> d, s = x.__getstate__()
+    >>> print_dict(d)
+    {}
+    >>> print_dict(s)
+    {'s1': 'x', 's2': 'y', 's3': 'z', 's4': 'spam'}
+
+    >>> pickle.loads(pickle.dumps(x)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 0)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 1)) == x
+    1
+    >>> pickle.loads(pickle.dumps(x, 2)) == x
+    1
+
+    """
+
+from doctest import DocTestSuite
+import unittest
+
+def test_suite():
+    return unittest.TestSuite((
+        DocTestSuite(),
+        ))
+
+if __name__ == '__main__': unittest.main()
+
+
+




More information about the Zodb-checkins mailing list