[Zodb-checkins] SVN: ZODB/trunk/src/ Officially deprecated PersistentDict

Jim Fulton jim at zope.com
Sat Aug 22 09:36:28 EDT 2009


Log message for revision 103082:
  Officially deprecated PersistentDict
  (https://bugs.launchpad.net/zodb/+bug/400775)
  
  Also, stop producing legacy PersistentMapping state, while still
  accepting it.
  

Changed:
  U   ZODB/trunk/src/CHANGES.txt
  U   ZODB/trunk/src/ZEO/tests/testZEO.py
  U   ZODB/trunk/src/ZODB/FileStorage/iterator.test
  U   ZODB/trunk/src/ZODB/scripts/fstail.txt
  U   ZODB/trunk/src/ZODB/tests/testCache.py
  U   ZODB/trunk/src/ZODB/tests/testConnectionSavepoint.py
  U   ZODB/trunk/src/ZODB/tests/testFileStorage.py
  U   ZODB/trunk/src/ZODB/tests/test_fsdump.py
  U   ZODB/trunk/src/ZODB/tests/testfsoids.py
  U   ZODB/trunk/src/persistent/dict.py
  U   ZODB/trunk/src/persistent/mapping.py
  U   ZODB/trunk/src/persistent/tests/test_mapping.py

-=-
Modified: ZODB/trunk/src/CHANGES.txt
===================================================================
--- ZODB/trunk/src/CHANGES.txt	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/CHANGES.txt	2009-08-22 13:36:27 UTC (rev 103082)
@@ -9,7 +9,11 @@
 ----------
 
 - The runzeo script didn't work without a configuration file.
+  (https://bugs.launchpad.net/zodb/+bug/410571)
 
+- Officially deprecated PersistentDict
+  (https://bugs.launchpad.net/zodb/+bug/400775)
+
 3.9.0b5 (2009-08-06)
 ====================
 

Modified: ZODB/trunk/src/ZEO/tests/testZEO.py
===================================================================
--- ZODB/trunk/src/ZEO/tests/testZEO.py	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/ZEO/tests/testZEO.py	2009-08-22 13:36:27 UTC (rev 103082)
@@ -774,7 +774,7 @@
     >>> from ZEO.StorageServer import StorageServer, ZEOStorage
     >>> from ZODB.FileStorage import FileStorage
     >>> from ZODB.DB import DB
-    >>> from persistent.dict import PersistentDict
+    >>> from persistent.mapping import PersistentMapping
     >>> from transaction import commit
     >>> fs1 = FileStorage('t1.fs')
     >>> fs2 = FileStorage('t2.fs')
@@ -789,17 +789,17 @@
     >>> commit()
     >>> o1 = conn1.root()
     >>> for i in range(10):
-    ...     o1.x = PersistentDict(); o1 = o1.x
+    ...     o1.x = PersistentMapping(); o1 = o1.x
     ...     commit()
 
     >>> last = fs1.lastTransaction()
     >>> for i in range(5):
-    ...     o1.x = PersistentDict(); o1 = o1.x
+    ...     o1.x = PersistentMapping(); o1 = o1.x
     ...     commit()
 
     >>> o2 = conn2.root()
     >>> for i in range(20):
-    ...     o2.x = PersistentDict(); o2 = o2.x
+    ...     o2.x = PersistentMapping(); o2 = o2.x
     ...     commit()
 
     >>> trans, oids = s1.getInvalidations(last)
@@ -822,14 +822,14 @@
     >>> from ZEO.StorageServer import StorageServer, ZEOStorage
     >>> from ZODB.FileStorage import FileStorage
     >>> from ZODB.DB import DB
-    >>> from persistent.dict import PersistentDict
+    >>> from persistent.mapping import PersistentMapping
     >>> fs = FileStorage('t.fs')
     >>> db = DB(fs)
     >>> conn = db.open()
     >>> from transaction import commit
     >>> last = []
     >>> for i in range(100):
-    ...     conn.root()[i] = PersistentDict()
+    ...     conn.root()[i] = PersistentMapping()
     ...     commit()
     ...     last.append(fs.lastTransaction())
     >>> db.close()
@@ -898,7 +898,7 @@
     >>> db = DB(st); conn = db.open()
     >>> ob = conn.root()
     >>> for i in range(5):
-    ...     ob.x = PersistentDict(); ob = ob.x
+    ...     ob.x = PersistentMapping(); ob = ob.x
     ...     commit()
     ...     last.append(fs.lastTransaction())
 

Modified: ZODB/trunk/src/ZODB/FileStorage/iterator.test
===================================================================
--- ZODB/trunk/src/ZODB/FileStorage/iterator.test	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/ZODB/FileStorage/iterator.test	2009-08-22 13:36:27 UTC (rev 103082)
@@ -66,12 +66,12 @@
     True
 
     >>> it = ZODB.FileStorage.FileIterator('data.fs', tids[70])
-    Scan backward data.fs:118274 looking for '\x03z\xbd\xd8\xed\xa7>\xcc'
+    Scan backward data.fs:117080 looking for '\x03z\xbd\xd8\xed\xa7>\xcc'
     >>> it.next().tid == tids[70]
     True
 
     >>> it = ZODB.FileStorage.FileIterator('data.fs', tids[-2])
-    Scan backward data.fs:118274 looking for '\x03z\xbd\xd8\xfa\x06\xd0\xcc'
+    Scan backward data.fs:117080 looking for '\x03z\xbd\xd8\xfa\x06\xd0\xcc'
     >>> it.next().tid == tids[-2]
     True
 
@@ -95,12 +95,12 @@
     True
 
     >>> it = ZODB.FileStorage.FileIterator('data.fs', tids[50], pos=poss[50])
-    Scan backward data.fs:36542 looking for '\x03z\xbd\xd8\xe5\x1e\xb6\xcc'
+    Scan backward data.fs:35936 looking for '\x03z\xbd\xd8\xe5\x1e\xb6\xcc'
     >>> it.next().tid == tids[50]
     True
 
     >>> it = ZODB.FileStorage.FileIterator('data.fs', tids[49], pos=poss[50])
-    Scan backward data.fs:36542 looking for '\x03z\xbd\xd8\xe4\xb1|\xcc'
+    Scan backward data.fs:35936 looking for '\x03z\xbd\xd8\xe4\xb1|\xcc'
     >>> it.next().tid == tids[49]
     True
 

Modified: ZODB/trunk/src/ZODB/scripts/fstail.txt
===================================================================
--- ZODB/trunk/src/ZODB/scripts/fstail.txt	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/ZODB/scripts/fstail.txt	2009-08-22 13:36:27 UTC (rev 103082)
@@ -24,10 +24,10 @@
   >>> from ZODB.scripts.fstail import main
   >>> main(storagefile, 5)
   2007-11-10 15:18:48.543001: hash=b16422d09fabdb45d4e4325e4b42d7d6f021d3c3
-  user='' description='' length=138 offset=191
+  user='' description='' length=132 offset=185
   <BLANKLINE>
   2007-11-10 15:18:48.543001: hash=b16422d09fabdb45d4e4325e4b42d7d6f021d3c3
-  user='' description='initial database creation' length=156 offset=52
+  user='' description='initial database creation' length=150 offset=52
   <BLANKLINE>
 
 Now clean up the storage again:

Modified: ZODB/trunk/src/ZODB/tests/testCache.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/testCache.py	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/ZODB/tests/testCache.py	2009-08-22 13:36:27 UTC (rev 103082)
@@ -433,7 +433,7 @@
 
     >>> getattr(conn.root, 'z', None)
     >>> conn._cache.total_estimated_size
-    128
+    64
 
 We add some data and the cache grows:
 

Modified: ZODB/trunk/src/ZODB/tests/testConnectionSavepoint.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/testConnectionSavepoint.py	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/ZODB/tests/testConnectionSavepoint.py	2009-08-22 13:36:27 UTC (rev 103082)
@@ -17,7 +17,7 @@
 """
 import unittest
 from zope.testing import doctest
-import persistent.dict
+import persistent.mapping
 import transaction
 
 def testAddingThenModifyThenAbort():
@@ -39,7 +39,7 @@
     >>> connection = db.open()
     >>> root = connection.root()
 
-    >>> ob = persistent.dict.PersistentDict()
+    >>> ob = persistent.mapping.PersistentMapping()
     >>> root['ob'] = ob
     >>> sp = transaction.savepoint()
     >>> ob.x = 1

Modified: ZODB/trunk/src/ZODB/tests/testFileStorage.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/testFileStorage.py	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/ZODB/tests/testFileStorage.py	2009-08-22 13:36:27 UTC (rev 103082)
@@ -509,10 +509,10 @@
     >>> fs = ZODB.FileStorage.FileStorage('t.fs', create=True)
     >>> db = DB(fs)
     >>> conn = db.open()
-    >>> from persistent.dict import PersistentDict
+    >>> from persistent.mapping import PersistentMapping
     >>> last = []
     >>> for i in range(100):
-    ...     conn.root()[i] = PersistentDict()
+    ...     conn.root()[i] = PersistentMapping()
     ...     transaction.commit()
     ...     last.append(fs.lastTransaction())
 

Modified: ZODB/trunk/src/ZODB/tests/test_fsdump.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/test_fsdump.py	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/ZODB/tests/test_fsdump.py	2009-08-22 13:36:27 UTC (rev 103082)
@@ -41,7 +41,7 @@
 >>> fsdump(path) #doctest: +ELLIPSIS
 Trans #00000 tid=... time=... offset=52
     status=' ' user='' description='initial database creation'
-  data #00000 oid=0000000000000000 size=66 class=persistent.mapping.PersistentMapping
+  data #00000 oid=0000000000000000 size=60 class=persistent.mapping.PersistentMapping
 
 Now we see first transaction with root object.
 
@@ -54,10 +54,10 @@
 >>> fsdump(path) #doctest: +ELLIPSIS
 Trans #00000 tid=... time=... offset=52
     status=' ' user='' description='initial database creation'
-  data #00000 oid=0000000000000000 size=66 class=persistent.mapping.PersistentMapping
-Trans #00001 tid=... time=... offset=207
+  data #00000 oid=0000000000000000 size=60 class=persistent.mapping.PersistentMapping
+Trans #00001 tid=... time=... offset=201
     status=' ' user='' description='added an OOBTree'
-  data #00000 oid=0000000000000000 size=113 class=persistent.mapping.PersistentMapping
+  data #00000 oid=0000000000000000 size=107 class=persistent.mapping.PersistentMapping
   data #00001 oid=0000000000000001 size=29 class=BTrees.OOBTree.OOBTree
 
 Now we see two transactions and two changed objects.

Modified: ZODB/trunk/src/ZODB/tests/testfsoids.py
===================================================================
--- ZODB/trunk/src/ZODB/tests/testfsoids.py	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/ZODB/tests/testfsoids.py	2009-08-22 13:36:27 UTC (rev 103082)
@@ -87,17 +87,17 @@
         tid user=''
         tid description='initial database creation'
         new revision persistent.mapping.PersistentMapping at 52
-    tid 0x... offset=168 ...
+    tid 0x... offset=162 ...
         tid user=''
         tid description='added an OOBTree'
-        new revision persistent.mapping.PersistentMapping at 207
-        references 0x01 BTrees.OOBTree.OOBTree at 207
+        new revision persistent.mapping.PersistentMapping at 201
+        references 0x01 BTrees.OOBTree.OOBTree at 201
 oid 0x01 BTrees.OOBTree.OOBTree 1 revision
-    tid 0x... offset=168 ...
+    tid 0x... offset=162 ...
         tid user=''
         tid description='added an OOBTree'
-        new revision BTrees.OOBTree.OOBTree at 362
-        referenced by 0x00 persistent.mapping.PersistentMapping at 207
+        new revision BTrees.OOBTree.OOBTree at 350
+        referenced by 0x00 persistent.mapping.PersistentMapping at 201
 
 So there are two revisions of oid 0 now, and the second references oid 1.
 
@@ -115,26 +115,26 @@
         tid user=''
         tid description='initial database creation'
         new revision persistent.mapping.PersistentMapping at 52
-    tid 0x... offset=168 ...
+    tid 0x... offset=162 ...
         tid user=''
         tid description='added an OOBTree'
-        new revision persistent.mapping.PersistentMapping at 207
-        references 0x01 BTrees.OOBTree.OOBTree at 207
-    tid 0x... offset=441 ...
+        new revision persistent.mapping.PersistentMapping at 201
+        references 0x01 BTrees.OOBTree.OOBTree at 201
+    tid 0x... offset=429 ...
         tid user=''
         tid description='circling back to the root'
-        referenced by 0x01 BTrees.OOBTree.OOBTree at 489
+        referenced by 0x01 BTrees.OOBTree.OOBTree at 477
 oid 0x01 BTrees.OOBTree.OOBTree 2 revisions
-    tid 0x... offset=168 ...
+    tid 0x... offset=162 ...
         tid user=''
         tid description='added an OOBTree'
-        new revision BTrees.OOBTree.OOBTree at 362
-        referenced by 0x00 persistent.mapping.PersistentMapping at 207
-    tid 0x... offset=441 ...
+        new revision BTrees.OOBTree.OOBTree at 350
+        referenced by 0x00 persistent.mapping.PersistentMapping at 201
+    tid 0x... offset=429 ...
         tid user=''
         tid description='circling back to the root'
-        new revision BTrees.OOBTree.OOBTree at 489
-        references 0x00 persistent.mapping.PersistentMapping at 489
+        new revision BTrees.OOBTree.OOBTree at 477
+        references 0x00 persistent.mapping.PersistentMapping at 477
 oid 0x02 <unknown> 0 revisions
     this oid was not defined (no data record for it found)
 

Modified: ZODB/trunk/src/persistent/dict.py
===================================================================
--- ZODB/trunk/src/persistent/dict.py	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/persistent/dict.py	2009-08-22 13:36:27 UTC (rev 103082)
@@ -1,6 +1,6 @@
 ##############################################################################
 #
-# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# Copyright Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
@@ -11,72 +11,6 @@
 # FOR A PARTICULAR PURPOSE.
 #
 ##############################################################################
-"""Python implementation of persistent container type
 
-$Id$
-"""
-
-import persistent
-from UserDict import IterableUserDict
-
-__metaclass__ = type
-
-class PersistentDict(persistent.Persistent, IterableUserDict):
-    """A persistent wrapper for mapping objects.
-
-    This class allows wrapping of mapping objects so that object
-    changes are registered.  As a side effect, mapping objects may be
-    subclassed.
-    """
-
-    # IterableUserDict provides all of the mapping behavior.  The
-    # PersistentDict class is responsible marking the persistent
-    # state as changed when a method actually changes the state.  At
-    # the mapping API evolves, we may need to add more methods here.
-
-    __super_delitem = IterableUserDict.__delitem__
-    __super_setitem = IterableUserDict.__setitem__
-    __super_clear = IterableUserDict.clear
-    __super_update = IterableUserDict.update
-    __super_setdefault = IterableUserDict.setdefault
-    __super_pop = IterableUserDict.pop
-    __super_popitem = IterableUserDict.popitem
-
-    __super_p_init = persistent.Persistent.__init__
-    __super_init = IterableUserDict.__init__
-
-    def __init__(self, dict=None):
-        self.__super_init(dict)
-        self.__super_p_init()
-
-    def __delitem__(self, key):
-        self.__super_delitem(key)
-        self._p_changed = True
-
-    def __setitem__(self, key, v):
-        self.__super_setitem(key, v)
-        self._p_changed = True
-
-    def clear(self):
-        self.__super_clear()
-        self._p_changed = True
-
-    def update(self, b):
-        self.__super_update(b)
-        self._p_changed = True
-
-    def setdefault(self, key, failobj=None):
-        # We could inline all of UserDict's implementation into the
-        # method here, but I'd rather not depend at all on the
-        # implementation in UserDict (simple as it is).
-        if not self.has_key(key):
-            self._p_changed = True
-        return self.__super_setdefault(key, failobj)
-
-    def pop(self, key, *args):
-        self._p_changed = True
-        return self.__super_pop(key, *args)
-
-    def popitem(self):
-        self._p_changed = True
-        return self.__super_popitem()
+# persistent.dict is deprecated. User persistent.mapping
+from persistent.mapping import PersistentMapping as PersistentDict

Modified: ZODB/trunk/src/persistent/mapping.py
===================================================================
--- ZODB/trunk/src/persistent/mapping.py	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/persistent/mapping.py	2009-08-22 13:36:27 UTC (rev 103082)
@@ -17,9 +17,20 @@
 $Id$"""
 
 import persistent
-from UserDict import UserDict
+import UserDict
 
-class PersistentMapping(UserDict, persistent.Persistent):
+class default(object):
+
+    def __init__(self, func):
+        self.func = func
+
+    def __get__(self, inst, class_):
+        if inst is None:
+            return self
+        return self.func(inst)
+
+
+class PersistentMapping(UserDict.IterableUserDict, persistent.Persistent):
     """A persistent wrapper for mapping objects.
 
     This class allows wrapping of mapping objects so that object
@@ -36,13 +47,13 @@
     # state as changed when a method actually changes the state.  At
     # the mapping API evolves, we may need to add more methods here.
 
-    __super_delitem = UserDict.__delitem__
-    __super_setitem = UserDict.__setitem__
-    __super_clear = UserDict.clear
-    __super_update = UserDict.update
-    __super_setdefault = UserDict.setdefault
-    __super_pop = UserDict.pop
-    __super_popitem = UserDict.popitem
+    __super_delitem = UserDict.IterableUserDict.__delitem__
+    __super_setitem = UserDict.IterableUserDict.__setitem__
+    __super_clear = UserDict.IterableUserDict.clear
+    __super_update = UserDict.IterableUserDict.update
+    __super_setdefault = UserDict.IterableUserDict.setdefault
+    __super_pop = UserDict.IterableUserDict.pop
+    __super_popitem = UserDict.IterableUserDict.popitem
 
     def __delitem__(self, key):
         self.__super_delitem(key)
@@ -76,38 +87,16 @@
         self._p_changed = 1
         return self.__super_popitem()
 
-    # __iter__ was added in ZODB 3.4.2, but should have been added long
-    # before.  We could inherit from Python's IterableUserDict instead
-    # (which just adds __iter__ to Python's UserDict), but that class isn't
-    # documented, and it would add another level of lookup for all the
-    # other methods.
-    def __iter__(self):
-        return iter(self.data)
+    # Old implementations used _container rather than data.
+    # Use a descriptor to provide data when we have _container instead
 
-    # If the internal representation of PersistentMapping changes,
-    # it causes compatibility problems for pickles generated by
-    # different versions of the code.  Compatibility works in both
-    # directions, because an application may want to share a database
-    # between applications using different versions of the code.
+    @default
+    def data(self):
+        # We don't want to cause a write on read, so wer're careful not to
+        # do anything that would cause us to become marked as changed, however,
+        # if we're modified, then the saved record will have data, not
+        # _container.
+        data = self.__dict__.pop('_container')
+        self.__dict__['data'] = data
 
-    # Effectively, the original rep is part of the "API."  To provide
-    # full compatibility, the getstate and setstate must read and
-    # write objects using the old rep.
-
-    # As a result, the PersistentMapping must save and restore the
-    # actual internal dictionary using the name _container.
-
-    def __getstate__(self):
-        state = dict([x for x in self.__dict__.items()
-                        if not x[0].startswith('_v_')])
-        state['_container'] = state['data']
-        del state['data']
-        return state
-
-    def __setstate__(self, state):
-        if state.has_key('_container'):
-            self.data = state['_container']
-            del state['_container']
-        elif not state.has_key('data'):
-            self.data = {}
-        self.__dict__.update(state)
+        return data

Modified: ZODB/trunk/src/persistent/tests/test_mapping.py
===================================================================
--- ZODB/trunk/src/persistent/tests/test_mapping.py	2009-08-22 12:21:41 UTC (rev 103081)
+++ ZODB/trunk/src/persistent/tests/test_mapping.py	2009-08-22 13:36:27 UTC (rev 103082)
@@ -1,21 +1,25 @@
 ##############################################################################
 #
-# Copyright (c) 2006 Zope Corporation and Contributors.
+# Copyright (c) Zope Foundation and Contributors.
 # All Rights Reserved.
 #
 # This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
+# 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 PersistentMapping
-"""
-
 import unittest
+from zope.testing import doctest, setupstack
 
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocFileSuite('README.txt'),
+        ))
+
+
 l0 = {}
 l1 = {0:0}
 l2 = {0:0, 1:1}
@@ -164,8 +168,54 @@
         u2.clear()
         eq(u2, {}, "u2 == {}")
 
+def test_legacy_data():
+    """
+We've deprecated PersistentDict.  If you import
+persistent.dict.PersistentDict, you'll get
+persistent.mapping.PersistentMapping.
+
+    >>> import persistent.dict, persistent.mapping
+    >>> persistent.dict.PersistentDict is persistent.mapping.PersistentMapping
+    True
+
+PersistentMapping uses a data attribute for it's mapping data:
+
+    >>> m = persistent.mapping.PersistentMapping()
+    >>> m.__dict__
+    {'data': {}}
+
+In the past, it used a _container attribute. For some time, the
+implementation continued to use a _container attribute in pickles
+(__get/setstate__) to be compatible with older releases.  This isn't
+really necessary any more. In fact, releases for which this might
+matter can no longer share databases with current releases.  Because
+releases as recent as 3.9.0b5 still use _container in saved state, we
+need to accept such state, but we stop producing it.
+
+If we reset it's __dict__ with legacy data:
+
+    >>> m.__dict__.clear()
+    >>> m.__dict__['_container'] = {'a': 1}
+    >>> m.__dict__
+    {'_container': {'a': 1}}
+    >>> m._p_changed = 0
+
+But when we perform any operations on it, the data will be converted
+without marking the object as changed:
+
+    >>> m
+    {'a': 1}
+    >>> m.__dict__
+    {'data': {'a': 1}}
+    >>> m._p_changed
+    0
+
+    >>> m.__getstate__()
+    {'data': {'a': 1}}
+    """
+
 def test_suite():
-    return unittest.makeSuite(MappingTests)
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')
+    return unittest.TestSuite((
+        doctest.DocTestSuite(),
+        unittest.makeSuite(MappingTests),
+        ))



More information about the Zodb-checkins mailing list