This is a pre-production deployment of Warehouse. Changes made here affect the production instance of PyPI (pypi.python.org).
Help us improve Python packaging - Donate today!
Project Description

zope.keyreference

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 implementer
>>> from zope.keyreference.interfaces import IKeyReference
>>> @implementer(IKeyReference)
... class DummyKeyReference(object):
...     key_type_id = 'zope.app.keyreference.object'
...     def __init__(self, obj):
...         self.object = obj
...     def _get_cmp_keys(self, other):
...          if self.key_type_id == other.key_type_id:
...              return self.object, other.object
...          return self.key_type_id, other.key_type_id
...     def __cmp__(self, other):
...         return cmp(*self._get_cmp_keys(other))
...     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
>>> dummy_key1 = DummyKeyReference(1)
>>> dummy_key2 = DummyKeyReference(2)
>>> dummy_key3 = DummyKeyReference(3)
>>> 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.

>>> def cmp(a, b):
...    return (a > b) - (a < b)
>>> cmp(factory(PersistentReference(
...         (b'an oid', 'class metadata'))),
...     factory(PersistentReference(
...         (b'an oid', 'class metadata'))))
0
>>> cmp(factory(PersistentReference(
...         (b'an oid', 'class metadata'))),
...     factory(PersistentReference(
...         (b'another oid', 'class metadata'))))
-1
>>> cmp(factory(PersistentReference(b'an oid')),
...     factory(PersistentReference(
...         (b'an oid', 'class metadata'))))
0
>>> cmp(factory(PersistentReference(b'an oid')),
...     factory(PersistentReference(
...         (b'an oid', 'class metadata'))))
0
>>> cmp(factory(PersistentReference(
...         ['m', ('a database', b'an oid', 'class metadata')])),
...     factory(PersistentReference(
...         ['m', ('a database', b'an oid', 'class metadata')])))
0
>>> cmp(factory(PersistentReference(
...         ['m', ('a database', b'an oid', 'class metadata')])),
...     factory(PersistentReference(
...         ['n', ('a database', b'an oid')])))
0
>>> cmp(factory(PersistentReference(
...         ['m', ('a database', b'an oid', 'class metadata')])),
...     factory(PersistentReference(
...         ['m', ('another database', b'an oid', 'class metadata')])))
-1
>>> cmp(factory(PersistentReference(
...         ['m', ('a database', b'an oid', 'class metadata')])),
...     factory(PersistentReference(
...         (b'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

4.1.0 (2014-12-27)

  • Add support for PyPy (PyPy3 blocked on PyPy3-compatible zodbpickle).
  • Add support for Python 3.4.

4.0.0 (2014-12-20)

  • Add support for testing on Travis.

4.0.0a2 (2013-02-25)

  • Ensure that the SimpleKeyReference implementation (used for testing) also implements rich comparison properly.

4.0.0a1 (2013-02-22)

  • Add support for Python 3.3.
  • Replace deprecated zope.component.adapts usage with equivalent zope.component.adapter decorator.
  • Replace deprecated zope.interface.implements usage with equivalent zope.interface.implementer decorator.
  • Drop support for Python 2.4 and 2.5.

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)

  • Make 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.
Release History

Release History

4.1.0

This version

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

4.0.0

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

4.0.0a2

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

4.0.0a1

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

3.6.4

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

3.6.3

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

3.6.2

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

3.6.1

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

3.6.0

History Node

TODO: Figure out how to actually get changelog content.

Changelog content for this version goes here.

Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit.

Show More

Download Files

Download Files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

File Name & Checksum SHA256 Checksum Help Version File Type Upload Date
zope.keyreference-4.1.0.tar.gz (16.5 kB) Copy SHA256 Checksum SHA256 Source Dec 27, 2014

Supported By

WebFaction WebFaction Technical Writing Elastic Elastic Search Pingdom Pingdom Monitoring Dyn Dyn DNS Sentry Sentry Error Logging CloudAMQP CloudAMQP RabbitMQ Heroku Heroku PaaS Kabu Creative Kabu Creative UX & Design Fastly Fastly CDN DigiCert DigiCert EV Certificate Rackspace Rackspace Cloud Servers DreamHost DreamHost Log Hosting