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

Jim Fulton cvs-admin at zope.org
Tue Nov 25 15:17:43 EST 2003


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

Added Files:
      Tag: Zope-2_8-devel-branch
	__init__.py testPersistent.py test_ExtensionClass.py 
Log Message:
merged everything but ZODB and ZEO from zodb33-devel-branch


=== Added File Zope/lib/python/Persistence/tests/__init__.py ===
# package


=== Added File Zope/lib/python/Persistence/tests/testPersistent.py ===
#############################################################################
#
# 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)


=== Added File Zope/lib/python/Persistence/tests/test_ExtensionClass.py ===
##############################################################################
#
# 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: test_ExtensionClass.py,v 1.1.4.1 2003/11/25 20:17:33 jim Exp $
"""

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