[Checkins] SVN: ZODB/branches/tseaver-python_picklecache-2/src/persistent/ Add more methods from the C class.

Tres Seaver tseaver at palladion.com
Tue Feb 15 17:08:26 EST 2011


Log message for revision 120358:
  Add more methods from the C class.

Changed:
  U   ZODB/branches/tseaver-python_picklecache-2/src/persistent/pypersistent.py
  U   ZODB/branches/tseaver-python_picklecache-2/src/persistent/tests/test_pypersistent.py

-=-
Modified: ZODB/branches/tseaver-python_picklecache-2/src/persistent/pypersistent.py
===================================================================
--- ZODB/branches/tseaver-python_picklecache-2/src/persistent/pypersistent.py	2011-02-15 19:13:38 UTC (rev 120357)
+++ ZODB/branches/tseaver-python_picklecache-2/src/persistent/pypersistent.py	2011-02-15 22:08:25 UTC (rev 120358)
@@ -36,6 +36,14 @@
 CHANGED = 1
 STICKY = 2
 
+# These names can be used from a ghost without causing it to be activated.
+SPECIAL_NAMES = ('__class__',
+                 '__del__',
+                 '__dict__',
+                 '__of__',
+                 '__setstate__'
+                )
+
 _SCONV = 60.0 / (1<<16) / (1<<16)
 
 def makeTimestamp(year, month, day, hour, minute, second):
@@ -54,7 +62,10 @@
     second = b * _SCONV
     return (year, month, day, hour, minute, second)
 
+
 class Persistent(object):
+    """ Pure Python implmentation of Persistent base class
+    """
     __slots__ = ('__jar', '__oid', '__serial', '__flags')
     implements(IPersistent)
 
@@ -201,12 +212,12 @@
     def __getstate__(self):
         """ See IPersistent.
         """
-        return {}
+        return ()
 
     def __setstate__(self, state):
         """ See IPersistent.
         """
-        if state != {}:
+        if state != ():
             raise ValueError('No state allowed on base Persistent class')
 
     def _p_activate(self):
@@ -230,6 +241,65 @@
             raise ValueError('Sticky')
         self.__flags = None
 
+    # Methods defined in C, not part of IPersistent
+    def _p_getattr(self, name):
+        """\
+        _p_getattr(name) -- Test whether the base class must handle the name
+   
+        The method unghostifies the object, if necessary.
+        The method records the object access, if necessary.
+ 
+        This method should be called by subclass __getattribute__
+        implementations before doing anything else. If the method
+        returns True, then __getattribute__ implementations must delegate
+        to the base class, Persistent.
+        """
+        if name.startswith('_p_') or name in SPECIAL_NAMES:
+            return True
+        self._p_activate()
+        # TODO set the object as acceessed with the jar's cache.
+        return False
+
+    def _p_setattr(self, name, value):
+        """_p_setattr(name, value) -- Save persistent meta data
+
+        This method should be called by subclass __setattr__ implementations
+        before doing anything else.  If it returns true, then the attribute
+        was handled by the base class.
+
+        The method unghostifies the object, if necessary.
+        The method records the object access, if necessary.
+        """
+        if name.startswith('_p_'):
+            setattr(self, name, value)
+            return True
+        self._p_activate()
+        # TODO set the object as acceessed with the jar's cache.
+        return False
+
+    def _p_delattr(self, name):
+        """_p_delattr(name) -- Delete persistent meta data
+
+        This method should be called by subclass __delattr__ implementations
+        before doing anything else.  If it returns true, then the attribute
+        was handled by the base class.
+
+        The method unghostifies the object, if necessary.
+        The method records the object access, if necessary.
+        """
+        if name.startswith('_p_'):
+            delattr(self, name)
+            return True
+        self._p_activate()
+        # TODO set the object as acceessed with the jar's cache.
+        return False
+
+    def __reduce__(self):
+        """Reduce an object to contituent parts for serialization.
+        """
+        gna = getattr(self, '__getnewargs__', lambda: ())
+        return ((type(self),) + gna(), self.__getstate__())
+
     # Helper methods:  not APIs
     def _register(self):
         if self.__jar is not None and self.__oid is not None:

Modified: ZODB/branches/tseaver-python_picklecache-2/src/persistent/tests/test_pypersistent.py
===================================================================
--- ZODB/branches/tseaver-python_picklecache-2/src/persistent/tests/test_pypersistent.py	2011-02-15 19:13:38 UTC (rev 120357)
+++ ZODB/branches/tseaver-python_picklecache-2/src/persistent/tests/test_pypersistent.py	2011-02-15 22:08:25 UTC (rev 120358)
@@ -36,7 +36,7 @@
                 self._registered.append(obj._p_oid)
         return _Jar()
 
-    def _makeOneWithJar(self, *args, **kw):
+    def _makeOneWithJar(self):
         OID = '1' * 8
         inst = self._makeOne()
         jar = self._makeJar()
@@ -421,11 +421,11 @@
 
     def test___getstate__(self):
         inst = self._makeOne()
-        self.assertEqual(inst.__getstate__(), {})
+        self.assertEqual(inst.__getstate__(), ())
 
     def test___setstate___empty(self):
         inst = self._makeOne()
-        inst.__setstate__({}) # doesn't raise, but doesn't change anything
+        inst.__setstate__(()) # doesn't raise, but doesn't change anything
 
     def test___setstate___nonempty(self):
         inst = self._makeOne()
@@ -543,3 +543,77 @@
         inst._p_changed = False
         inst._p_sticky = True
         self.assertRaises(ValueError, inst._p_invalidate)
+
+    def test__p_getattr_w__p__name(self):
+        inst, jar, OID = self._makeOneWithJar()
+        self.failUnless(inst._p_getattr('_p_foo'))
+        self.assertEqual(inst._p_status, 'ghost')
+        self.assertEqual(list(jar._loaded), [])
+
+    def test__p_getattr_w_special_names(self):
+        from persistent.pypersistent import SPECIAL_NAMES
+        inst, jar, OID = self._makeOneWithJar()
+        for name in SPECIAL_NAMES:
+            self.failUnless(inst._p_getattr(name))
+            self.assertEqual(inst._p_status, 'ghost')
+        self.assertEqual(list(jar._loaded), [])
+
+    def test__p_getattr_w_normal_name(self):
+        inst, jar, OID = self._makeOneWithJar()
+        self.failIf(inst._p_getattr('normal'))
+        self.assertEqual(inst._p_status, 'saved')
+        self.assertEqual(list(jar._loaded), [OID])
+
+    def test__p_setattr_w__p__name(self):
+        inst, jar, OID = self._makeOneWithJar()
+        self.failUnless(inst._p_setattr('_p_serial', '1' * 8))
+        self.assertEqual(inst._p_status, 'ghost')
+        self.assertEqual(inst._p_serial, '1' * 8)
+        self.assertEqual(list(jar._loaded), [])
+
+    def test__p_setattr_w_normal_name(self):
+        inst, jar, OID = self._makeOneWithJar()
+        self.failIf(inst._p_setattr('normal', 'value'))
+        self.assertEqual(inst._p_status, 'saved')
+        self.assertEqual(list(jar._loaded), [OID])
+
+    def test__p_delattr_w__p__name(self):
+        inst, jar, OID = self._makeOneWithJar()
+        inst._p_changed = True
+        jar._loaded = []
+        self.failUnless(inst._p_delattr('_p_changed'))
+        self.assertEqual(inst._p_status, 'ghost')
+        self.assertEqual(inst._p_changed, None)
+        self.assertEqual(list(jar._loaded), [])
+
+    def test__p_delattr_w_normal_name(self):
+        inst, jar, OID = self._makeOneWithJar()
+        self.failIf(inst._p_delattr('normal'))
+        self.assertEqual(inst._p_status, 'saved')
+        self.assertEqual(list(jar._loaded), [OID])
+
+    def test___reduce__(self):
+        inst = self._makeOne()
+        first, second = inst.__reduce__()
+        self.assertEqual(first, (self._getTargetClass(),))
+        self.assertEqual(second, ())
+
+    def test___reduce__w_subclass_having_getstate(self):
+        class Derived(self._getTargetClass()):
+            def __getstate__(self):
+                return {}
+        inst = Derived()
+        first, second = inst.__reduce__()
+        self.assertEqual(first, (Derived,))
+        self.assertEqual(second, {})
+
+    def test___reduce__w_subclass_having_gna_and_getstate(self):
+        class Derived(self._getTargetClass()):
+            def __getnewargs__(self):
+                return ('a', 'b')
+            def __getstate__(self):
+                return {'foo': 'bar'}
+        inst = Derived()
+        first, second = inst.__reduce__()
+        self.assertEqual(first, (Derived, 'a', 'b'))
+        self.assertEqual(second, {'foo': 'bar'})



More information about the checkins mailing list