[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