[Checkins] SVN: zope.app.keyreference/trunk/ convert zope.app.keyreference.persistent to understand ZODB conflict error placeholder class, PersistentReference. Relies on ZODB changes in trunk/3.9 line.

Gary Poster gary at zope.com
Sun Jun 24 14:45:44 EDT 2007


Log message for revision 77018:
  convert zope.app.keyreference.persistent to understand ZODB conflict error placeholder class, PersistentReference.  Relies on ZODB changes in trunk/3.9 line.

Changed:
  U   zope.app.keyreference/trunk/README.txt
  U   zope.app.keyreference/trunk/buildout.cfg
  U   zope.app.keyreference/trunk/setup.py
  A   zope.app.keyreference/trunk/src/zope/app/keyreference/CHANGES.txt
  U   zope.app.keyreference/trunk/src/zope/app/keyreference/persistent.py
  U   zope.app.keyreference/trunk/src/zope/app/keyreference/persistent.txt

-=-
Modified: zope.app.keyreference/trunk/README.txt
===================================================================
--- zope.app.keyreference/trunk/README.txt	2007-06-24 18:25:05 UTC (rev 77017)
+++ zope.app.keyreference/trunk/README.txt	2007-06-24 18:45:44 UTC (rev 77018)
@@ -1,4 +1,4 @@
 zope.app.keyreference
 =====================
 
-See, src/zope/app/keyreference/README.txt 
+Object references that support stable comparison and hashes.

Modified: zope.app.keyreference/trunk/buildout.cfg
===================================================================
--- zope.app.keyreference/trunk/buildout.cfg	2007-06-24 18:25:05 UTC (rev 77017)
+++ zope.app.keyreference/trunk/buildout.cfg	2007-06-24 18:45:44 UTC (rev 77018)
@@ -1,7 +1,13 @@
 [buildout]
 develop = .
-parts = test
+parts = test py
+find-links = http://download.zope.org/distribution/
 
 [test]
 recipe = zc.recipe.testrunner
 eggs = zope.app.keyreference
+
+[py]
+recipe = zc.recipe.egg
+eggs = zope.app.keyreference
+interpreter = py

Modified: zope.app.keyreference/trunk/setup.py
===================================================================
--- zope.app.keyreference/trunk/setup.py	2007-06-24 18:25:05 UTC (rev 77017)
+++ zope.app.keyreference/trunk/setup.py	2007-06-24 18:45:44 UTC (rev 77018)
@@ -21,7 +21,7 @@
 from setuptools import setup, find_packages
 
 setup(name = 'zope.app.keyreference',
-      version = '3.4.0b1',
+      version = '3.5.0-dev',
       url = 'http://svn.zope.org/zope.app.keyreference',
       license = 'ZPL 2.1',
       description = 'Zope app.keyreference',
@@ -34,7 +34,7 @@
 
       namespace_packages = ['zope', 'zope.app'],
       install_requires = ['setuptools',
-                          'ZODB3',
+                          'ZODB3>=3.9.0-dev-r77011',
                           'zope.component',
                           'zope.i18nmessageid',
                           'zope.interface',

Added: zope.app.keyreference/trunk/src/zope/app/keyreference/CHANGES.txt
===================================================================
--- zope.app.keyreference/trunk/src/zope/app/keyreference/CHANGES.txt	                        (rev 0)
+++ zope.app.keyreference/trunk/src/zope/app/keyreference/CHANGES.txt	2007-06-24 18:45:44 UTC (rev 77018)
@@ -0,0 +1,6 @@
+3.5.0 (unreleased)
+
+- Added support for new ZODB.ConflictResolution.PersistentReference behavior
+  to persistent key references so that they can now, in many cases, allow
+  conflict resolution when they are used as keys or set members in ZODB
+  `BTrees` data structures.


Property changes on: zope.app.keyreference/trunk/src/zope/app/keyreference/CHANGES.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: zope.app.keyreference/trunk/src/zope/app/keyreference/persistent.py
===================================================================
--- zope.app.keyreference/trunk/src/zope/app/keyreference/persistent.py	2007-06-24 18:25:05 UTC (rev 77017)
+++ zope.app.keyreference/trunk/src/zope/app/keyreference/persistent.py	2007-06-24 18:45:44 UTC (rev 77018)
@@ -18,6 +18,7 @@
 $Id$
 """
 from ZODB.interfaces import IConnection
+from ZODB.ConflictResolution import PersistentReference
 import zope.interface
 
 import zope.app.keyreference.interfaces
@@ -46,16 +47,48 @@
         return self.object
 
     def __hash__(self):
-        return hash((self.object._p_jar.db().database_name,
-                     self.object._p_oid,
-                     ))
+        if isinstance(self.object, PersistentReference):
+            # we are doing conflict resolution.
+            database_name = self.object.database_name
+            if database_name is None:
+                # we can't hash
+                raise ValueError('database name unavailable at this time')
+            oid = self.object.oid
+        else:
+            database_name = self.object._p_jar.db().database_name
+            oid = self.object._p_oid
+        return hash((database_name, oid))
 
     def __cmp__(self, other):
         if self.key_type_id == other.key_type_id:
-            return cmp(
-                (self.object._p_jar.db().database_name,  self.object._p_oid),
-                (other.object._p_jar.db().database_name, other.object._p_oid),
-                )
+            if isinstance(self.object, PersistentReference):
+                # we are doing conflict resolution.
+                assert isinstance(other.object, PersistentReference), (
+                    'other object claims to be '
+                    'zope.app.keyreference.persistent but, during conflict '
+                    'resolution, object is not a PersistentReference')
+                self_name = self.object.database_name
+                other_name = other.object.database_name
+                if (self_name is None) ^ (other_name is None):
+                    # one of the two database_names are None during conflict
+                    # resolution.  At this time the database_name is
+                    # inaccessible, not unset (it is the same database as the
+                    # object being resolved).  If they were both None, we
+                    # would know they are from the same database, so we can
+                    # compare the oids.  If neither were None, we would be
+                    # able to reliably compare.  However, in this case,
+                    # one is None and the other is not, so we can't know how
+                    # they would sort outside of conflict resolution.  Give
+                    # up.
+                    raise ValueError('cannot sort reliably')
+                self_oid = self.object.oid
+                other_oid = other.object.oid
+            else:
+                self_name = self.object._p_jar.db().database_name
+                self_oid = self.object._p_oid
+                other_name = other.object._p_jar.db().database_name
+                other_oid = other.object._p_oid
+            return cmp((self_name, self_oid), (other_name, other_oid))
 
         return cmp(self.key_type_id, other.key_type_id)
 

Modified: zope.app.keyreference/trunk/src/zope/app/keyreference/persistent.txt
===================================================================
--- zope.app.keyreference/trunk/src/zope/app/keyreference/persistent.txt	2007-06-24 18:25:05 UTC (rev 77017)
+++ zope.app.keyreference/trunk/src/zope/app/keyreference/persistent.txt	2007-06-24 18:45:44 UTC (rev 77018)
@@ -134,7 +134,100 @@
     >>> key3 = KeyReferenceToPersistent(ob3)
     >>> transaction.abort()
 
+Conflict Resolution
+-------------------
 
+During conflict resolution, as discussed in ZODB/ConflictResolution.txt,
+references to persistent objects are actually instances of
+ZODB.ConflictResolution.PersistentReference.  This is pertinent in two ways
+for KeyReferenceToPersistent.  First, it explains a subtlety of the class: it
+does not inherit from persistent.Persistent.  If it did, it would not be
+available for conflict resolution, just its PersistentReference stand-in.
+
+Second, it explains some of the code in the __hash__ and __cmp__
+methods. These methods not only handle persistent.Persistent objects,
+but PersistentReference objects.  Without this behavior, objects, such
+as the classic ZODB BTrees, that use KeyReferenceToPersistent as keys or
+set members will be unable to resolve conflicts.  Even with the special
+code, in some cases the KeyReferenceToPersistent will refuse to compare
+and hash during conflict resolution because it cannot reliably do so.
+
+__hash__ will work relatively rarely during conflict resolution: only for
+multidatabase references.  Here are a couple of examples.
+
+    >>> from ZODB.ConflictResolution import PersistentReference
+
+    >>> def factory(ref):
+    ...     res = KeyReferenceToPersistent.__new__(
+    ...         KeyReferenceToPersistent, ref)
+    ...     res.object = ref
+    ...     return res
+    ...
+
+    >>> hash(factory(PersistentReference(
+    ...     ('an oid', 'class metadata')))) # a typical reference
+    Traceback (most recent call last):
+    ...
+    ValueError: database name unavailable at this time
+    
+    >>> bool(hash(factory(PersistentReference(
+    ...     ['m', ('a database', 'an oid', 'class metadata')])))) # multidatabase
+    True
+
+This means that KeyReferenceToPersistent will often hinder conflict resolution
+for classes such as PersistentDict.
+
+__cmp__ works unless one object is a multidatabase reference and the other is
+not.  Here are a few examples.
+
+    >>> cmp(factory(PersistentReference(
+    ...         ('an oid', 'class metadata'))),
+    ...     factory(PersistentReference(
+    ...         ('an oid', 'class metadata'))))
+    0
+
+    >>> cmp(factory(PersistentReference(
+    ...         ('an oid', 'class metadata'))),
+    ...     factory(PersistentReference(
+    ...         ('another oid', 'class metadata'))))
+    -1
+
+    >>> cmp(factory(PersistentReference('an oid')),
+    ...     factory(PersistentReference(
+    ...         ('an oid', 'class metadata'))))
+    0
+
+    >>> cmp(factory(PersistentReference('an oid')),
+    ...     factory(PersistentReference(
+    ...         ('an oid', 'class metadata'))))
+    0
+
+    >>> cmp(factory(PersistentReference(
+    ...         ['m', ('a database', 'an oid', 'class metadata')])),
+    ...     factory(PersistentReference(
+    ...         ['m', ('a database', 'an oid', 'class metadata')])))
+    0
+
+    >>> cmp(factory(PersistentReference(
+    ...         ['m', ('a database', 'an oid', 'class metadata')])),
+    ...     factory(PersistentReference(
+    ...         ['n', ('a database', 'an oid')])))
+    0
+
+    >>> cmp(factory(PersistentReference(
+    ...         ['m', ('a database', 'an oid', 'class metadata')])),
+    ...     factory(PersistentReference(
+    ...         ['m', ('another database', 'an oid', 'class metadata')])))
+    -1
+
+    >>> cmp(factory(PersistentReference(
+    ...         ['m', ('a database', 'an oid', 'class metadata')])),
+    ...     factory(PersistentReference(
+    ...         ('an oid', 'class metadata'))))
+    Traceback (most recent call last):
+    ...
+    ValueError: cannot sort reliably
+
 Location-based connection adapter
 ---------------------------------
 



More information about the Checkins mailing list