zope.keyreference-3.6.4/0000775000177100020040000000000011665401000016266 5ustar menesismenesis00000000000000zope.keyreference-3.6.4/setup.py0000664000177100020040000000520411665400764020021 0ustar menesismenesis00000000000000############################################################################## # # Copyright (c) 2006 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. # ############################################################################## # This package is developed by the Zope Toolkit project, documented here: # http://docs.zope.org/zopetoolkit # When developing and releasing this package, please follow the documented # Zope Toolkit policies as described by this documentation. ############################################################################## """Setup for zope.keyreference package """ import os from setuptools import setup, find_packages def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() setup(name = 'zope.keyreference', version='3.6.4', author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', description='Key References', long_description=( read('README.txt') + '\n\n' + 'Detailed Documentation\n' + '----------------------\n' + '\n\n' + read('src', 'zope', 'keyreference', 'persistent.txt') + '\n\n' + read('CHANGES.txt') ), keywords = "zope3 key reference persistent", classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: Zope Public License', 'Programming Language :: Python', 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', 'Framework :: Zope3'], url='http://pypi.python.org/pypi/zope.keyreference', license='ZPL 2.1', packages=find_packages('src'), package_dir = {'': 'src'}, namespace_packages=['zope'], extras_require={'test': []}, install_requires = ['setuptools', 'ZODB3', 'zope.component', 'zope.i18nmessageid', 'zope.interface', 'zope.schema', ], include_package_data = True, zip_safe = False, ) zope.keyreference-3.6.4/CHANGES.txt0000664000177100020040000000140011665400764020112 0ustar menesismenesis00000000000000======= CHANGES ======= 3.6.4 (2011-11-30) ------------------ - Fix tests broken by removal of ``zope.testing`` from test dependencies: avoid the ``ZODB3`` module that needs it. 3.6.3 (2011-11-29) ------------------ - Prefer the standard libraries doctest module to the one from ``zope.testing``. 3.6.2 (2009-09-15) ------------------ - Made the tests pass with ZODB3.9, which changed the repr() of the persistent classes. 3.6.1 (2009-02-01) ------------------ - Load keyreferences, pickled by old zope.app.keyreference even if its not installed anymore (so don't break if one updates a project that don't directly depends on zope.app.keyreference). 3.6.0 (2009-01-31) ------------------ - Rename ``zope.app.keyreference`` to ``zope.keyreference``. zope.keyreference-3.6.4/PKG-INFO0000664000177100020040000002731011665401000017366 0ustar menesismenesis00000000000000Metadata-Version: 1.0 Name: zope.keyreference Version: 3.6.4 Summary: Key References Home-page: http://pypi.python.org/pypi/zope.keyreference Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: Object references that support stable comparison and hashes. Detailed Documentation ---------------------- ===================================== 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.MappingStorage 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 ======= CHANGES ======= 3.6.4 (2011-11-30) ------------------ - Fix tests broken by removal of ``zope.testing`` from test dependencies: avoid the ``ZODB3`` module that needs it. 3.6.3 (2011-11-29) ------------------ - Prefer the standard libraries doctest module to the one from ``zope.testing``. 3.6.2 (2009-09-15) ------------------ - Made the tests pass with ZODB3.9, which changed the repr() of the persistent classes. 3.6.1 (2009-02-01) ------------------ - Load keyreferences, pickled by old zope.app.keyreference even if its not installed anymore (so don't break if one updates a project that don't directly depends on zope.app.keyreference). 3.6.0 (2009-01-31) ------------------ - Rename ``zope.app.keyreference`` to ``zope.keyreference``. Keywords: zope3 key reference persistent Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope3 zope.keyreference-3.6.4/bootstrap.py0000664000177100020040000000330211665400764020673 0ustar menesismenesis00000000000000############################################################################## # # Copyright (c) 2006 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. # ############################################################################## """Bootstrap a buildout-based project Simply run this script in a directory containing a buildout.cfg. The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. """ import os, shutil, sys, tempfile, urllib2 tmpeggs = tempfile.mkdtemp() ez = {} exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) import pkg_resources cmd = 'from setuptools.command.easy_install import main; main()' if sys.platform == 'win32': cmd = '"%s"' % cmd # work around spawn lamosity on windows ws = pkg_resources.working_set assert os.spawnle( os.P_WAIT, sys.executable, sys.executable, '-c', cmd, '-mqNxd', tmpeggs, 'zc.buildout', dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse('setuptools')).location ), ) == 0 ws.add_entry(tmpeggs) ws.require('zc.buildout') import zc.buildout.buildout zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap']) shutil.rmtree(tmpeggs) zope.keyreference-3.6.4/COPYRIGHT.txt0000664000177100020040000000004011665400764020411 0ustar menesismenesis00000000000000Zope Foundation and Contributorszope.keyreference-3.6.4/src/0000775000177100020040000000000011665401000017055 5ustar menesismenesis00000000000000zope.keyreference-3.6.4/src/zope.keyreference.egg-info/0000775000177100020040000000000011665401000024172 5ustar menesismenesis00000000000000zope.keyreference-3.6.4/src/zope.keyreference.egg-info/PKG-INFO0000664000177100020040000002731011665400773025312 0ustar menesismenesis00000000000000Metadata-Version: 1.0 Name: zope.keyreference Version: 3.6.4 Summary: Key References Home-page: http://pypi.python.org/pypi/zope.keyreference Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: Object references that support stable comparison and hashes. Detailed Documentation ---------------------- ===================================== 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.MappingStorage 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 ======= CHANGES ======= 3.6.4 (2011-11-30) ------------------ - Fix tests broken by removal of ``zope.testing`` from test dependencies: avoid the ``ZODB3`` module that needs it. 3.6.3 (2011-11-29) ------------------ - Prefer the standard libraries doctest module to the one from ``zope.testing``. 3.6.2 (2009-09-15) ------------------ - Made the tests pass with ZODB3.9, which changed the repr() of the persistent classes. 3.6.1 (2009-02-01) ------------------ - Load keyreferences, pickled by old zope.app.keyreference even if its not installed anymore (so don't break if one updates a project that don't directly depends on zope.app.keyreference). 3.6.0 (2009-01-31) ------------------ - Rename ``zope.app.keyreference`` to ``zope.keyreference``. Keywords: zope3 key reference persistent Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope3 zope.keyreference-3.6.4/src/zope.keyreference.egg-info/dependency_links.txt0000664000177100020040000000000111665400773030260 0ustar menesismenesis00000000000000 zope.keyreference-3.6.4/src/zope.keyreference.egg-info/namespace_packages.txt0000664000177100020040000000000511665400773030540 0ustar menesismenesis00000000000000zope zope.keyreference-3.6.4/src/zope.keyreference.egg-info/not-zip-safe0000664000177100020040000000000111665400765026441 0ustar menesismenesis00000000000000 zope.keyreference-3.6.4/src/zope.keyreference.egg-info/top_level.txt0000664000177100020040000000000511665400773026737 0ustar menesismenesis00000000000000zope zope.keyreference-3.6.4/src/zope.keyreference.egg-info/requires.txt0000664000177100020040000000012611665400773026611 0ustar menesismenesis00000000000000setuptools ZODB3 zope.component zope.i18nmessageid zope.interface zope.schema [test] zope.keyreference-3.6.4/src/zope.keyreference.egg-info/SOURCES.txt0000664000177100020040000000123611665400773026100 0ustar menesismenesis00000000000000CHANGES.txt COPYRIGHT.txt LICENSE.txt README.txt bootstrap.py buildout.cfg setup.py src/zope/__init__.py src/zope.keyreference.egg-info/PKG-INFO src/zope.keyreference.egg-info/SOURCES.txt src/zope.keyreference.egg-info/dependency_links.txt src/zope.keyreference.egg-info/namespace_packages.txt src/zope.keyreference.egg-info/not-zip-safe src/zope.keyreference.egg-info/requires.txt src/zope.keyreference.egg-info/top_level.txt src/zope/keyreference/__init__.py src/zope/keyreference/configure.zcml src/zope/keyreference/interfaces.py src/zope/keyreference/persistent.py src/zope/keyreference/persistent.txt src/zope/keyreference/testing.py src/zope/keyreference/tests.pyzope.keyreference-3.6.4/src/zope/0000775000177100020040000000000011665401000020032 5ustar menesismenesis00000000000000zope.keyreference-3.6.4/src/zope/keyreference/0000775000177100020040000000000011665401000022501 5ustar menesismenesis00000000000000zope.keyreference-3.6.4/src/zope/keyreference/configure.zcml0000664000177100020040000000151111665400764025367 0ustar menesismenesis00000000000000 zope.keyreference-3.6.4/src/zope/keyreference/tests.py0000664000177100020040000000333511665400764024241 0ustar menesismenesis00000000000000############################################################################## # # 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 import doctest def test_multi_databases(): """ >>> from ZODB.MappingStorage 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') zope.keyreference-3.6.4/src/zope/keyreference/persistent.py0000664000177100020040000001227611665400764025303 0ustar menesismenesis00000000000000############################################################################## # # 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) @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 zope.keyreference-3.6.4/src/zope/keyreference/__init__.py0000664000177100020040000000123111665400764024627 0ustar menesismenesis00000000000000############################################################################## # # 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. # ############################################################################## """Key references """ zope.keyreference-3.6.4/src/zope/keyreference/testing.py0000664000177100020040000000260211665400764024550 0ustar menesismenesis00000000000000############################################################################## # # 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) zope.keyreference-3.6.4/src/zope/keyreference/interfaces.py0000664000177100020040000000313711665400764025222 0ustar menesismenesis00000000000000############################################################################## # # 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. """ zope.keyreference-3.6.4/src/zope/keyreference/persistent.txt0000664000177100020040000001763111665400764025472 0ustar menesismenesis00000000000000===================================== 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.MappingStorage 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 zope.keyreference-3.6.4/src/zope/__init__.py0000664000177100020040000000007011665400764022160 0ustar menesismenesis00000000000000__import__('pkg_resources').declare_namespace(__name__) zope.keyreference-3.6.4/buildout.cfg0000664000177100020040000000026411665400764020620 0ustar menesismenesis00000000000000[buildout] develop = . parts = test py [test] recipe = zc.recipe.testrunner eggs = zope.keyreference [test] [py] recipe = zc.recipe.egg eggs = zope.keyreference interpreter = py zope.keyreference-3.6.4/README.txt0000664000177100020040000000007511665400764020006 0ustar menesismenesis00000000000000Object references that support stable comparison and hashes. zope.keyreference-3.6.4/setup.cfg0000664000177100020040000000007311665401000020107 0ustar menesismenesis00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 zope.keyreference-3.6.4/LICENSE.txt0000664000177100020040000000402611665400764020133 0ustar menesismenesis00000000000000Zope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.