Source code for zope.keyreference.persistent

##############################################################################
#
# 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


[docs]@zope.interface.implementer(zope.keyreference.interfaces.IKeyReference) 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. """ 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 _get_cmp_keys(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 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 (self_name, self_oid), (other_name, other_oid) return self.key_type_id, other.key_type_id def __eq__(self, other): a, b = self._get_cmp_keys(other) return a == b def __lt__(self, other): a, b = self._get_cmp_keys(other) return a < b def __ne__(self, other): a, b = self._get_cmp_keys(other) return a != b def __gt__(self, other): a, b = self._get_cmp_keys(other) return a > b def __le__(self, other): a, b = self._get_cmp_keys(other) return a <= b def __ge__(self, other): a, b = self._get_cmp_keys(other) return a >= b
[docs]@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