[Checkins] SVN: Sandbox/J1m/zc.persistentkeyreference/src/zc/ checkpoint
Jim Fulton
jim at zope.com
Fri Nov 18 17:54:29 UTC 2011
Log message for revision 123425:
checkpoint
Changed:
A Sandbox/J1m/zc.persistentkeyreference/src/zc/
D Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/configure.zcml
A Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/configure.zcml
D Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/interfaces.py
D Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.py
A Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.py
D Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.txt
A Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.txt
D Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/testing.py
D Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/tests.py
A Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/tests.py
-=-
Deleted: Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/configure.zcml
===================================================================
--- Sandbox/J1m/zc.persistentkeyreference/src/zope/keyreference/configure.zcml 2011-11-18 17:21:17 UTC (rev 123423)
+++ Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/configure.zcml 2011-11-18 17:54:29 UTC (rev 123425)
@@ -1,32 +0,0 @@
-<configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
-
- <adapter
- for="persistent.interfaces.IPersistent"
- factory=".persistent.KeyReferenceToPersistent"
- trusted="y"
- />
-
- <class class=".persistent.KeyReferenceToPersistent">
- <require permission="zope.Public" interface=".interfaces.IKeyReference" />
- </class>
-
- <adapter
- for="persistent.interfaces.IPersistent"
- factory=".persistent.connectionOfPersistent"
- />
-
- <!-- Registering documentation with API doc -->
- <configure
- xmlns:apidoc="http://namespaces.zope.org/apidoc"
- xmlns:zcml="http://namespaces.zope.org/zcml"
- zcml:condition="have apidoc">
-
- <apidoc:bookchapter
- id="keyref"
- title="Persistent Key References"
- doc_path="persistent.txt"
- />
-
- </configure>
-
-</configure>
Copied: Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/configure.zcml (from rev 123424, Sandbox/J1m/zc.persistentkeyreference/src/zope/keyreference/configure.zcml)
===================================================================
--- Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/configure.zcml (rev 0)
+++ Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/configure.zcml 2011-11-18 17:54:29 UTC (rev 123425)
@@ -0,0 +1,27 @@
+<configure xmlns="http://namespaces.zope.org/zope" i18n_domain="zope">
+
+ <adapter
+ for="persistent.interfaces.IPersistent"
+ factory=".persistent.KeyReferenceToPersistent"
+ trusted="y"
+ />
+
+ <class class=".persistent.KeyReferenceToPersistent">
+ <require permission="zope.Public" interface=".interfaces.IKeyReference" />
+ </class>
+
+ <!-- <\!-- Registering documentation with API doc -\-> -->
+ <!-- <configure -->
+ <!-- xmlns:apidoc="http://namespaces.zope.org/apidoc" -->
+ <!-- xmlns:zcml="http://namespaces.zope.org/zcml" -->
+ <!-- zcml:condition="have apidoc"> -->
+
+ <!-- <apidoc:bookchapter -->
+ <!-- id="keyref" -->
+ <!-- title="Persistent Key References" -->
+ <!-- doc_path="persistent.txt" -->
+ <!-- /> -->
+
+ <!-- </configure> -->
+
+</configure>
Deleted: Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/interfaces.py
===================================================================
--- Sandbox/J1m/zc.persistentkeyreference/src/zope/keyreference/interfaces.py 2011-11-18 17:21:17 UTC (rev 123423)
+++ Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/interfaces.py 2011-11-18 17:54:29 UTC (rev 123425)
@@ -1,52 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 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.
-# 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.
-#
-##############################################################################
-"""Key-reference interfaces
-"""
-import zope.interface
-from zope.schema import DottedName
-from zope.i18nmessageid import MessageFactory
-
-_ = MessageFactory('zope')
-
-
-class NotYet(Exception):
- """Can't compute a key reference for an object
-
- It might be possible to compute one later
- (e.g. at the end of the transaction).
- """
-
-class IKeyReference(zope.interface.Interface):
- """A reference to an object (similar to a weak reference).
-
- The references are compared by their hashes.
- """
-
- key_type_id = DottedName(title=_('Key Type Id'),
- description=_('Key references should sort first '
- 'on their key type and second on any type-specific '
- 'information.')
- )
-
- def __call__():
- """Get the object this reference is linking to.
- """
-
- def __hash__():
- """Get a unique identifier of the referenced object.
- """
-
- def __cmp__(ref):
- """Compare the reference to another reference.
- """
Deleted: Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.py
===================================================================
--- Sandbox/J1m/zc.persistentkeyreference/src/zope/keyreference/persistent.py 2011-11-18 17:21:17 UTC (rev 123423)
+++ Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.py 2011-11-18 17:54:29 UTC (rev 123425)
@@ -1,127 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2001, 2002 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.
-# 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.
-#
-##############################################################################
-"""KeyReference for persistent objects.
-
-Provides an IKeyReference adapter for persistent objects.
-"""
-from ZODB.interfaces import IConnection
-from ZODB.ConflictResolution import PersistentReference
-import zope.interface
-
-import zope.keyreference.interfaces
-
-class KeyReferenceToPersistent(object):
- """An IKeyReference for persistent objects which is comparable.
-
- These references compare by database name and _p_oids of the objects they
- reference.
- """
- zope.interface.implements(zope.keyreference.interfaces.IKeyReference)
-
- key_type_id = 'zope.app.keyreference.persistent'
-
- def __init__(self, object):
- if not getattr(object, '_p_oid', None):
- connection = IConnection(object, None)
- if connection is None:
- raise zope.keyreference.interfaces.NotYet(object)
-
- connection.add(object)
-
- self.object = object
-
- def __call__(self):
- return self.object
-
- def __hash__(self):
- 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:
- # While it makes subclassing this class inconvenient,
- # comparing the object's type is faster than doing an
- # isinstance check. The intent of using type instead
- # of isinstance is to avoid loading state just to
- # determine if we're in conflict resolution.
- if type(self.object) is 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)
-
-
- at zope.interface.implementer(IConnection)
-def connectionOfPersistent(ob):
- """An adapter which gets a ZODB connection of a persistent object.
-
- We are assuming the object has a parent if it has been created in
- this transaction.
-
- Raises ValueError if it is impossible to get a connection.
- """
- cur = ob
- while not getattr(cur, '_p_jar', None):
- cur = getattr(cur, '__parent__', None)
- if cur is None:
- return None
- return cur._p_jar
-
-# BBB: If zope.app.keyreference is not installed, we still want
-# old key references to be available. So fake a module to make
-# them unpickleable.
-try:
- import zope.app.keyreference
-except ImportError:
- import sys
- from types import ModuleType as module
- z_a_k = module('zope.app.keyreference')
- sys.modules['zope.app.keyreference'] = z_a_k
- z_a_k_p = module('zope.app.keyreference.persistent')
- z_a_k_p.KeyReferenceToPersistent = KeyReferenceToPersistent
- sys.modules['zope.app.keyreference.persistent'] = z_a_k_p
Copied: Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.py (from rev 123424, Sandbox/J1m/zc.persistentkeyreference/src/zope/keyreference/persistent.py)
===================================================================
--- Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.py (rev 0)
+++ Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.py 2011-11-18 17:54:29 UTC (rev 123425)
@@ -0,0 +1,66 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+# 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.
+#
+##############################################################################
+"""KeyReference for persistent objects.
+
+Provides an IKeyReference adapter for persistent objects.
+"""
+from ZODB.interfaces import IConnection
+from ZODB.ConflictResolution import PersistentReference
+import zope.interface
+
+import zope.keyreference.interfaces
+import zope.keyreference.persistent
+
+class KeyReferenceToPersistent(
+ zope.keyreference.persistent.KeyReferenceToPersistent):
+
+ key_type_id = 'zc.persistentkeyreference'
+
+ def __cmp__(self, other):
+ if self.key_type_id == other.key_type_id:
+ # While it makes subclassing this class inconvenient,
+ # comparing the object's type is faster than doing an
+ # isinstance check. The intent of using type instead
+ # of isinstance is to avoid loading state just to
+ # determine if we're in conflict resolution.
+ if type(self.object) is 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)
Deleted: Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.txt
===================================================================
--- Sandbox/J1m/zc.persistentkeyreference/src/zope/keyreference/persistent.txt 2011-11-18 17:21:17 UTC (rev 123423)
+++ Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.txt 2011-11-18 17:54:29 UTC (rev 123425)
@@ -1,246 +0,0 @@
-=====================================
-Key References for Persistent Objects
-=====================================
-
-`zope.keyreference.persistent.KeyReferenceToPersistent` provides an
-`zope.keyreference.interfaces.IKeyReference` reference for persistent
-objects.
-
-Let's look at an example. First, we'll create some persistent objects
-in a database:
-
- >>> from ZODB.tests.util import DB
- >>> import transaction
- >>> from persistent.mapping import PersistentMapping
-
- >>> db = DB()
- >>> conn = db.open()
- >>> root = conn.root()
-
- >>> root['ob1'] = PersistentMapping()
- >>> root['ob2'] = PersistentMapping()
-
- >>> transaction.commit()
-
-Then we'll create some key references:
-
- >>> from zope.keyreference.persistent import KeyReferenceToPersistent
-
- >>> key1 = KeyReferenceToPersistent(root['ob1'])
- >>> key2 = KeyReferenceToPersistent(root['ob2'])
-
-We can call the keys to get the objects:
-
- >>> key1() is root['ob1'], key2() is root['ob2']
- (True, True)
-
-New keys to the same objects are equal to the old:
-
- >>> KeyReferenceToPersistent(root['ob1']) == key1
- True
-
-and have the same hashes:
-
- >>> hash(KeyReferenceToPersistent(root['ob1'])) == hash(key1)
- True
-
-Other key reference implementations are differed by their key type id.
-Key references should sort first on their key type and second on any
-type-specific information:
-
- >>> from zope.interface import implements
- >>> from zope.keyreference.interfaces import IKeyReference
-
- >>> class DummyKeyReference(object):
- ... implements(IKeyReference)
- ... key_type_id = 'zope.app.keyreference.object'
- ... def __init__(self, obj):
- ... self.object = obj
- ... def __cmp__(self, other):
- ... if self.key_type_id == other.key_type_id:
- ... return cmp(self.object, other.object)
- ... return cmp(self.key_type_id, other.key_type_id)
-
- >>> dummy_key1 = DummyKeyReference(object())
- >>> dummy_key2 = DummyKeyReference(object())
- >>> dummy_key3 = DummyKeyReference(object())
-
- >>> keys = [key1, dummy_key1, dummy_key2, key2, dummy_key3]
- >>> keys.sort()
- >>> key_type_ids = [key.key_type_id for key in keys]
- >>> key_type_ids[0:3].count('zope.app.keyreference.object')
- 3
- >>> key_type_ids[3:].count('zope.app.keyreference.persistent')
- 2
-
-We'll store the key references in the database:
-
- >>> root['key1'] = key1
- >>> root['key2'] = key2
-
-and use the keys to store the objects again:
-
- >>> root[key1] = root['ob1']
- >>> root[key2] = root['ob2']
-
- >>> transaction.commit()
-
-Now we'll open another connection:
-
- >>> conn2 = db.open()
-
-And verify that we can use the keys to look up the objects:
-
- >>> root2 = conn2.root()
- >>> key1 = root2['key1']
- >>> root2[key1] is root2['ob1']
- True
- >>> key2 = root2['key2']
- >>> root2[key2] is root2['ob2']
- True
-
-and that we can also call the keys to get the objects:
-
- >>> key1() is root2['ob1']
- True
- >>> key2() is root2['ob2']
- True
-
-We can't get the key reference for an object that hasn't been saved
-yet:
-
- >>> KeyReferenceToPersistent(PersistentMapping())
- ... # doctest: +ELLIPSIS
- Traceback (most recent call last):
- ...
- NotYet: ...
-
-Note that we get a NotYet error. This indicates that we might be able
-to get a key reference later.
-
-We can get references to unsaved objects if they have an adapter to
-`ZODB.interfaces.IConnection`. The `add` method on the connection
-will be used to give the object an object id, which is enough
-information to compute the reference. To see this, we'll create an
-object that conforms to `IConnection` in a silly way:
-
- >>> import persistent
- >>> from ZODB.interfaces import IConnection
- >>> class C(persistent.Persistent):
- ... def __conform__(self, iface):
- ... if iface is IConnection:
- ... return conn2
-
- >>> ob3 = C()
- >>> 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 PersistentMapping.
-
-__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
----------------------------------
-
-The function `zope.keyreference.connectionOfPersistent` adapts
-objects to connections using a simple location-based heuristic. It
-checked to see if the object has a `__parent__` that has a connection:
-
- >>> from zope.keyreference.persistent import connectionOfPersistent
- >>> ob3 = PersistentMapping()
- >>> print connectionOfPersistent(ob3)
- None
-
- >>> ob3.__parent__ = root2['ob1']
- >>> connectionOfPersistent(ob3) is conn2
- True
Copied: Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.txt (from rev 123424, Sandbox/J1m/zc.persistentkeyreference/src/zope/keyreference/persistent.txt)
===================================================================
--- Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.txt (rev 0)
+++ Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/persistent.txt 2011-11-18 17:54:29 UTC (rev 123425)
@@ -0,0 +1,246 @@
+==================================================
+New Improved Key References for Persistent Objects
+==================================================
+
+`zc.persistentkeyreference.KeyReferenceToPersistent` provides an
+`zope.keyreference.interfaces.IKeyReference` reference for persistent
+objects.
+
+Let's look at an example. First, we'll create some persistent objects
+in a database:
+
+ >>> from ZODB.tests.util import DB
+ >>> import transaction
+ >>> from persistent.mapping import PersistentMapping
+
+ >>> db = DB()
+ >>> conn = db.open()
+ >>> root = conn.root()
+
+ >>> root['ob1'] = PersistentMapping()
+ >>> root['ob2'] = PersistentMapping()
+
+ >>> transaction.commit()
+
+Then we'll create some key references:
+
+ >>> from zope.keyreference.persistent import KeyReferenceToPersistent
+
+ >>> key1 = KeyReferenceToPersistent(root['ob1'])
+ >>> key2 = KeyReferenceToPersistent(root['ob2'])
+
+We can call the keys to get the objects:
+
+ >>> key1() is root['ob1'], key2() is root['ob2']
+ (True, True)
+
+New keys to the same objects are equal to the old:
+
+ >>> KeyReferenceToPersistent(root['ob1']) == key1
+ True
+
+and have the same hashes:
+
+ >>> hash(KeyReferenceToPersistent(root['ob1'])) == hash(key1)
+ True
+
+Other key reference implementations are differed by their key type id.
+Key references should sort first on their key type and second on any
+type-specific information:
+
+ >>> from zope.interface import implements
+ >>> from zope.keyreference.interfaces import IKeyReference
+
+ >>> class DummyKeyReference(object):
+ ... implements(IKeyReference)
+ ... key_type_id = 'zope.app.keyreference.object'
+ ... def __init__(self, obj):
+ ... self.object = obj
+ ... def __cmp__(self, other):
+ ... if self.key_type_id == other.key_type_id:
+ ... return cmp(self.object, other.object)
+ ... return cmp(self.key_type_id, other.key_type_id)
+
+ >>> dummy_key1 = DummyKeyReference(object())
+ >>> dummy_key2 = DummyKeyReference(object())
+ >>> dummy_key3 = DummyKeyReference(object())
+
+ >>> keys = [key1, dummy_key1, dummy_key2, key2, dummy_key3]
+ >>> keys.sort()
+ >>> key_type_ids = [key.key_type_id for key in keys]
+ >>> key_type_ids[0:3].count('zope.app.keyreference.object')
+ 3
+ >>> key_type_ids[3:].count('zope.app.keyreference.persistent')
+ 2
+
+We'll store the key references in the database:
+
+ >>> root['key1'] = key1
+ >>> root['key2'] = key2
+
+and use the keys to store the objects again:
+
+ >>> root[key1] = root['ob1']
+ >>> root[key2] = root['ob2']
+
+ >>> transaction.commit()
+
+Now we'll open another connection:
+
+ >>> conn2 = db.open()
+
+And verify that we can use the keys to look up the objects:
+
+ >>> root2 = conn2.root()
+ >>> key1 = root2['key1']
+ >>> root2[key1] is root2['ob1']
+ True
+ >>> key2 = root2['key2']
+ >>> root2[key2] is root2['ob2']
+ True
+
+and that we can also call the keys to get the objects:
+
+ >>> key1() is root2['ob1']
+ True
+ >>> key2() is root2['ob2']
+ True
+
+We can't get the key reference for an object that hasn't been saved
+yet:
+
+ >>> KeyReferenceToPersistent(PersistentMapping())
+ ... # doctest: +ELLIPSIS
+ Traceback (most recent call last):
+ ...
+ NotYet: ...
+
+Note that we get a NotYet error. This indicates that we might be able
+to get a key reference later.
+
+We can get references to unsaved objects if they have an adapter to
+`ZODB.interfaces.IConnection`. The `add` method on the connection
+will be used to give the object an object id, which is enough
+information to compute the reference. To see this, we'll create an
+object that conforms to `IConnection` in a silly way:
+
+ >>> import persistent
+ >>> from ZODB.interfaces import IConnection
+ >>> class C(persistent.Persistent):
+ ... def __conform__(self, iface):
+ ... if iface is IConnection:
+ ... return conn2
+
+ >>> ob3 = C()
+ >>> 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 PersistentMapping.
+
+__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
+---------------------------------
+
+The function `zope.keyreference.connectionOfPersistent` adapts
+objects to connections using a simple location-based heuristic. It
+checked to see if the object has a `__parent__` that has a connection:
+
+ >>> from zope.keyreference.persistent import connectionOfPersistent
+ >>> ob3 = PersistentMapping()
+ >>> print connectionOfPersistent(ob3)
+ None
+
+ >>> ob3.__parent__ = root2['ob1']
+ >>> connectionOfPersistent(ob3) is conn2
+ True
Deleted: Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/testing.py
===================================================================
--- Sandbox/J1m/zc.persistentkeyreference/src/zope/keyreference/testing.py 2011-11-18 17:21:17 UTC (rev 123423)
+++ Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/testing.py 2011-11-18 17:54:29 UTC (rev 123425)
@@ -1,41 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2001, 2002 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.
-# 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.
-#
-##############################################################################
-"""Testing components
-"""
-import zope.interface
-import zope.component
-import zope.keyreference.interfaces
-
-class SimpleKeyReference(object):
- """An IReference for all objects. This implementation is *not* ZODB safe.
- """
- zope.component.adapts(zope.interface.Interface)
- zope.interface.implements(zope.keyreference.interfaces.IKeyReference)
-
- key_type_id = 'zope.app.keyreference.simple'
-
- def __init__(self, object):
- self.object = object
-
- def __call__(self):
- return self.object
-
- def __hash__(self):
- return hash(self.object)
-
- def __cmp__(self, other):
- if self.key_type_id == other.key_type_id:
- return cmp(hash(self.object), hash(other))
-
- return cmp(self.key_type_id, other.key_type_id)
Deleted: Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/tests.py
===================================================================
--- Sandbox/J1m/zc.persistentkeyreference/src/zope/keyreference/tests.py 2011-11-18 17:21:17 UTC (rev 123423)
+++ Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/tests.py 2011-11-18 17:54:29 UTC (rev 123425)
@@ -1,59 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2001, 2002 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.
-# 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.
-#
-##############################################################################
-"""Tests for the unique id utility.
-"""
-import unittest
-from zope.testing import doctest
-
-def test_multi_databases():
- """
- >>> from ZODB.tests.util import DB
- >>> import transaction
- >>> from BTrees.OOBTree import OOBucket
-
- >>> databases = {}
-
- >>> db1 = DB(databases=databases, database_name='1')
- >>> db2 = DB(databases=databases, database_name='2')
-
- >>> conn1 = db1.open()
- >>> conn1.root()['ob'] = OOBucket()
-
- >>> conn2 = conn1.get_connection('2')
- >>> conn2.root()['ob'] = OOBucket()
-
- >>> conn1.root()['ob']._p_oid == conn2.root()['ob']._p_oid
- True
-
- >>> transaction.commit()
-
- >>> from zope.keyreference.persistent import KeyReferenceToPersistent
-
- >>> key1 = KeyReferenceToPersistent(conn1.root()['ob'])
- >>> key2 = KeyReferenceToPersistent(conn2.root()['ob'])
-
- >>> key1 != key2, key2 > key1, hash(key1) != hash(key2)
- (True, True, True)
-
-"""
-
-def test_suite():
- return unittest.TestSuite((
- doctest.DocFileSuite('persistent.txt'),
- doctest.DocTestSuite(),
- ))
-
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
-
Copied: Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/tests.py (from rev 123424, Sandbox/J1m/zc.persistentkeyreference/src/zope/keyreference/tests.py)
===================================================================
--- Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/tests.py (rev 0)
+++ Sandbox/J1m/zc.persistentkeyreference/src/zc/keyreference/tests.py 2011-11-18 17:54:29 UTC (rev 123425)
@@ -0,0 +1,59 @@
+##############################################################################
+#
+# Copyright (c) 2001, 2002 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.
+# 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.
+#
+##############################################################################
+"""Tests for the unique id utility.
+"""
+import unittest
+from zope.testing import doctest
+
+def test_multi_databases():
+ """
+ >>> from ZODB.tests.util import DB
+ >>> import transaction
+ >>> from BTrees.OOBTree import OOBucket
+
+ >>> databases = {}
+
+ >>> db1 = DB(databases=databases, database_name='1')
+ >>> db2 = DB(databases=databases, database_name='2')
+
+ >>> conn1 = db1.open()
+ >>> conn1.root()['ob'] = OOBucket()
+
+ >>> conn2 = conn1.get_connection('2')
+ >>> conn2.root()['ob'] = OOBucket()
+
+ >>> conn1.root()['ob']._p_oid == conn2.root()['ob']._p_oid
+ True
+
+ >>> transaction.commit()
+
+ >>> from zc.persistentkeyreference import KeyReferenceToPersistent
+
+ >>> key1 = KeyReferenceToPersistent(conn1.root()['ob'])
+ >>> key2 = KeyReferenceToPersistent(conn2.root()['ob'])
+
+ >>> key1 != key2, key2 > key1, hash(key1) != hash(key2)
+ (True, True, True)
+
+"""
+
+def test_suite():
+ return unittest.TestSuite((
+ doctest.DocFileSuite('persistent.txt'),
+ doctest.DocTestSuite(),
+ ))
+
+if __name__ == '__main__':
+ unittest.main(defaultTest='test_suite')
+
More information about the checkins
mailing list