[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