SVN: ZODB/trunk/src/ Officially deprecated PersistentDict

Jim Fulton <[email protected]>
Newsgroups gmane.comp.python.zope.zodb.cvs
Message-ID <[email protected]>
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),
+        ))
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.