[Zope3-checkins] CVS: Zope3/src/persistence - wref.py:1.2

Jim Fulton jim at zope.com
Wed Jan 7 07:12:55 EST 2004


Update of /cvs-repository/Zope3/src/persistence
In directory cvs.zope.org:/tmp/cvs-serv11523/src/persistence

Modified Files:
	wref.py 
Log Message:
Added words to the weakref test.

Added basic PersistentWeakKeyDictionary objects.


=== Zope3/src/persistence/wref.py 1.1 => 1.2 ===
--- Zope3/src/persistence/wref.py:1.1	Tue Jan  6 14:50:38 2004
+++ Zope3/src/persistence/wref.py	Wed Jan  7 07:12:53 2004
@@ -16,6 +16,8 @@
 $Id$
 """
 
+from persistence import Persistent
+
 WeakRefMarker = object()
 
 class WeakRef(object):
@@ -26,7 +28,8 @@
     specify an object to be called when the object is removed from the
     database.
 
-    Here's an example:
+    Here's an example. We'll start by creating a persistent object and
+    a refernce to it:
 
     >>> import persistence.list
     >>> import zodb.tests.util
@@ -35,6 +38,22 @@
     >>> ref() is ob
     True
 
+    The hash of the ref if the same as the hash of the refernced object:
+
+    >>> hash(ref) == hash(ob)
+    True
+
+    Two refs to the same object are equal:
+
+    >>> WeakRef(ob) == ref
+    True
+    
+    >>> ob2 = persistence.list.PersistentList([1])
+    >>> WeakRef(ob2) == ref
+    False
+
+    Lets save the refernce and the refernced object in a database:
+
     >>> db = zodb.tests.util.DB()
     
     >>> conn1 = db.open()
@@ -42,22 +61,43 @@
     >>> conn1.root()['ref'] = ref
     >>> zodb.tests.util.commit()
 
+    If we oprn a new connection, sure enough, we can use the reference:
+
     >>> conn2 = db.open()
-    >>> conn1.root()['ref']() is conn1.root()['ob']
+    >>> conn2.root()['ref']() is conn2.root()['ob']
+    True
+    >>> hash(conn2.root()['ref']) == hash(conn2.root()['ob'])
     True
 
-    >>> del conn1.root()['ob']
+    But if we delete the referenced object and pack:
+
+    >>> del conn2.root()['ob']
     >>> zodb.tests.util.commit()
     >>> zodb.tests.util.pack(db)
 
+    And then look in a new connection:
+
     >>> conn3 = db.open()
-    >>> conn3.root()['ref']()
     >>> conn3.root()['ob']
     Traceback (most recent call last):
     ...
     KeyError: 'ob'
 
+    Trying to dereference the refernce returns None:
+    
+    >>> conn3.root()['ref']()
+    
+    Trying to get a hash, raises a type error:
+
+    >>> hash(conn3.root()['ref'])
+    Traceback (most recent call last):
+    ...
+    TypeError: Weakly-referenced object has gone away
+
+    Always explicitly close databases: :)
+    
     >>> db.close()
+
     """
 
     # We set _p_oid to a merker so that the serialization system can
@@ -78,4 +118,160 @@
             except KeyError:
                 return None
             return self._v_ob
+
+    def __hash__(self):
+        self = self()
+        if self is None:
+            raise TypeError('Weakly-referenced object has gone away')
+        return hash(self)
+
+    def __eq__(self, other):
+        self = self()
+        if self is None:
+            raise TypeError('Weakly-referenced object has gone away')
+        other = other()
+        if other is None:
+            raise TypeError('Weakly-referenced object has gone away')
+
+        return self == other
+    
             
+class PersistentWeakKeyDictionary(Persistent):
+    """Persistent weak key dictionary
+
+    This is akin to WeakKeyDictionaries. Note, however, that removal
+    of items is extremely lazy. See below.
+
+    We'll start by creating a PersistentWeakKeyDictionary and adding
+    some persistent objects to it.
+
+    >>> d = PersistentWeakKeyDictionary()
+    >>> import zodb.tests.util
+    >>> p1 = zodb.tests.util.P('p1')
+    >>> p2 = zodb.tests.util.P('p2')
+    >>> p3 = zodb.tests.util.P('p3')
+    >>> d[p1] = 1
+    >>> d[p2] = 2
+    >>> d[p3] = 3
+
+    We'll create an extra persustent object that's not in the dict:
+
+    >>> p4 = zodb.tests.util.P('p4')
+
+    Now we'll excercise iteration and item access:
+
+    >>> l = [(str(k), d[k], d.get(k)) for k in d]
+    >>> l.sort()
+    >>> l
+    [('P(p1)', 1, 1), ('P(p2)', 2, 2), ('P(p3)', 3, 3)]
+
+    And the containment operator:
+
+    >>> [p in d for p in [p1, p2, p3, p4]]
+    [True, True, True, False]
+
+    We can add the dict and the refernced objects to a database:
+    
+    >>> db = zodb.tests.util.DB()
+    
+    >>> conn1 = db.open()
+    >>> conn1.root()['p1'] = p1
+    >>> conn1.root()['d'] = d
+    >>> conn1.root()['p2'] = p2
+    >>> conn1.root()['p3'] = p3
+    >>> zodb.tests.util.commit()
+
+    And things still work, as before:
+
+    >>> l = [(str(k), d[k], d.get(k)) for k in d]
+    >>> l.sort()
+    >>> l
+    [('P(p1)', 1, 1), ('P(p2)', 2, 2), ('P(p3)', 3, 3)]
+    >>> [p in d for p in [p1, p2, p3, p4]]
+    [True, True, True, False]
+
+    Likewise, we can read the objects from another connection and
+    things still work.
+
+    >>> conn2 = db.open()
+    >>> d = conn2.root()['d']
+    >>> p1 = conn2.root()['p1']
+    >>> p2 = conn2.root()['p2']
+    >>> p3 = conn2.root()['p3']
+    >>> l = [(str(k), d[k], d.get(k)) for k in d]
+    >>> l.sort()
+    >>> l
+    [('P(p1)', 1, 1), ('P(p2)', 2, 2), ('P(p3)', 3, 3)]
+    >>> [p in d for p in [p1, p2, p3, p4]]
+    [True, True, True, False]
+
+    Now, we'll delete one of the objects from the database, but *not*
+    from the dictionary:
+
+    >>> del conn2.root()['p2']
+    >>> zodb.tests.util.commit()
+
+    And pack the database, so that the no-longer referenced p2 is
+    actuallt removed from the database.
+
+    >>> zodb.tests.util.pack(db)
+
+    Now if we access the dictionary in a new connection, it no longer
+    has p2:
+    
+    >>> conn3 = db.open()
+    >>> d = conn3.root()['d']
+    >>> l = [(str(k), d[k], d.get(k)) for k in d]
+    >>> l.sort()
+    >>> l
+    [('P(p1)', 1, 1), ('P(p3)', 3, 3)]
+
+    It's worth nothing that that the versions of the dictionary in
+    conn1 and conn2 still have p2, because p2 is still in the caches
+    for those connections. 
+
+    Always explicitly close databases: :)
+    
+    >>> db.close()
+
+    """
+    # XXX it is expensive trying to load dead objects from the database.
+    #     It would be helpful if the data manager/connection cached these.
+
+    
+    def __init__(self):
+        self.data = {}
+
+    def __getstate__(self):
+        state = Persistent.__getstate__(self)
+        state['data'] = state['data'].items()
+        return state
+
+    def __setstate__(self, state):
+        state['data'] = dict([
+            (k, v) for (k, v) in state['data']
+            if k() is not None
+            ])
+        Persistent.__setstate__(self, state)
+        
+    def __setitem__(self, key, value):
+        self.data[WeakRef(key)] = value
+        
+    def __getitem__(self, key):
+        return self.data[WeakRef(key)]
+        
+    def __delitem__(self, key):
+        del self.data[WeakRef(key)]
+
+    def get(self, key):
+        return self.data.get(WeakRef(key))
+
+    def __contains__(self, key):
+        return WeakRef(key) in self.data
+    
+    def __iter__(self):
+        for k in self.data:
+            yield k()
+
+    # XXX Someone else can fill out the rest of the methods, with tests. :)
+    




More information about the Zope3-Checkins mailing list