[Checkins] SVN: zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/ refactor: PersistentReferenceProxy instead of PersistentReferenceSet

Andrew Sung asung at zope.com
Wed Jan 11 00:12:15 UTC 2012


Log message for revision 124025:
  refactor: PersistentReferenceProxy instead of PersistentReferenceSet
  
  

Changed:
  U   zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/_queue.py
  U   zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/queue.txt
  U   zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/tests.py

-=-
Modified: zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/_queue.py
===================================================================
--- zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/_queue.py	2012-01-10 23:32:13 UTC (rev 124024)
+++ zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/_queue.py	2012-01-11 00:12:14 UTC (rev 124025)
@@ -52,8 +52,8 @@
 PersistentQueue = BucketQueue  # for legacy instances, be conservative
 
 
-class PersistentReferenceSet(object):
-    """PersistentReferenceSet
+class PersistentReferenceProxy(object):
+    """PersistentReferenceProxy
 
     `ZODB.ConflictResolution.PersistentReference` doesn't get handled correctly
     in the resolveQueueConflict function due to lack of the `__hash__` method.
@@ -61,95 +61,23 @@
     `PersistentReference`.
 
     """
-    def __init__(self, seq):
-        assert isinstance(seq, tuple)
-        self._data = self._dedup(seq)
+    def __init__(self, pr):
+        assert isinstance(pr, PersistentReference)
+        self.pr = pr
 
-    def _dedup(self, seq):
-        seq = list(seq)
-        cnt = 0
-        while len(seq) > cnt:
-            remove = []
-            for idx, item in enumerate(seq[cnt + 1:]):
-                try:
-                    if item == seq[cnt]:
-                        remove.append(cnt + idx + 1)
-                except ValueError:
-                    pass
-            for idx in reversed(remove):
-                seq.pop(idx)
-            cnt += 1
-        return tuple(seq)
+    def __hash__(self):
+        return 1
 
-    def __cmp__(self, other):
-        if len(self._data) == len(other._data):
-            other_data = list(other._data[:])
-            for item in self._data:
-                for index, other_item in enumerate(other_data):
-                    try:
-                        if item == other_item:
-                            other_data.pop(index)
-                    except ValueError:
-                        pass
-                    else:
-                        break
-                else:
-                    break
-            else:
-                assert len(other_data) == 0
-                return 0
-        raise ValueError(
-            "can't reliably compare against different "
-            "PersistentReferences")
+    def __eq__(self, other):
+        try:
+            return self.pr == other.pr
+        except ValueError:
+            return False
 
-    def __sub__(self, other):
-        self_data = list(self._data[:])
-        for other_item in other._data:
-            for index, item in enumerate(self_data):
-                try:
-                    if other_item == item:
-                        self_data.pop(index)
-                except ValueError:
-                    pass
-                else:
-                    break
-        return PersistentReferenceSet(tuple(self_data))
-
-    def __and__(self, other):
-        self_data = list(self._data[:])
-        intersection = []
-        for other_item in other._data:
-            for index, item in enumerate(self_data):
-                try:
-                    if other_item == item:
-                        self_data.pop(index)
-                        intersection.append(item)
-                except ValueError:
-                    pass
-                else:
-                    break
-        return PersistentReferenceSet(tuple(intersection))
-
-    def __iter__(self):
-        for item in self._data:
-            yield item
-
-    def __len__(self):
-        return len(self._data)
-
     def __repr__(self):
-        return "PRSet(%s)" % str(self._data)
+        return str(self.pr)
 
-    def __contains__(self, item):
-        for data in self._data:
-            try:
-                if item == data:
-                    return True
-            except ValueError:
-                pass
-        return False
 
-
 def resolveQueueConflict(oldstate, committedstate, newstate, bucket=False):
     # we only know how to merge _data.  If anything else is different,
     # puke.
@@ -161,22 +89,18 @@
     # basically, we are ok with anything--willing to merge--
     # unless committedstate and newstate have one or more of the
     # same deletions or additions in comparison to the oldstate.
-    old = oldstate['_data']
-    committed = committedstate['_data']
-    new = newstate['_data']
 
-    # If items in the queue are persistent object, we can't use set().
-    # see 'queue.txt'
-    for item in (old + committed + new):
-        if not isinstance(item, PersistentReference):
-            Set = set
-            break
-    else:
-        Set = PersistentReferenceSet
+    # If items in the queue are persistent object, we need to wrap
+    # PersistentReference objects. See 'queue.txt'
+    wrap = lambda x: (
+        PersistentReferenceProxy(x) if isinstance(x, PersistentReference) else x)
+    old = map(wrap, oldstate['_data'])
+    committed = map(wrap, committedstate['_data'])
+    new = map(wrap, newstate['_data'])
 
-    old_set = Set(old)
-    committed_set = Set(committed)
-    new_set = Set(new)
+    old_set = set(old)
+    committed_set = set(committed)
+    new_set = set(new)
 
     if bucket and bool(old_set) and (bool(committed_set) ^ bool(new_set)):
         # This is a bucket, part of a CompositePersistentQueue.  The old set
@@ -201,13 +125,14 @@
     # Now we do the merge.  We'll merge into the committed state and
     # return it.
     mod_committed = []
+    unwrap = lambda x: x.pr if isinstance(x, PersistentReferenceProxy) else x
     for v in committed:
         if v not in new_removed:
-            mod_committed.append(v)
+            mod_committed.append(unwrap(v))
     if new_added:
         ordered_new_added = new[-len(new_added):]
-        assert Set(ordered_new_added) == new_added
-        mod_committed.extend(ordered_new_added)
+        assert set(ordered_new_added) == new_added
+        mod_committed.extend(map(unwrap, ordered_new_added))
     committedstate['_data'] = tuple(mod_committed)
     return committedstate
 

Modified: zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/queue.txt
===================================================================
--- zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/queue.txt	2012-01-10 23:32:13 UTC (rev 124024)
+++ zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/queue.txt	2012-01-11 00:12:14 UTC (rev 124025)
@@ -398,28 +398,26 @@
     >>> db.close() # cleanup
 
 
-======================
-PersistentReferenceSet
-======================
+========================
+PersistentReferenceProxy
+========================
 
 As `ZODB.ConflictResolution.PersistentReference` doesn't get handled
 properly in `set` due to lack of `__hash__` method, we define a class
 utilizing `__cmp__` method of contained items [#workaround]_.
 
 
-Let's make some Stubbed persistent reference object.
+Let's make some stub persistent reference objects. Also make some
+objects that have same oid to simulate different transaction states.
 
     >>> from zc.queue.tests import StubPersistentReference
     >>> pr1 = StubPersistentReference(1)
     >>> pr2 = StubPersistentReference(2)
     >>> pr3 = StubPersistentReference(3)
+    >>> pr_c1 = StubPersistentReference(1)
+    >>> pr_c2 = StubPersistentReference(2)
+    >>> pr_c3 = StubPersistentReference(3)
 
-Let's make copy of them to simulate different transaction states.
-
-    >>> import copy
-    >>> pr_c1 = copy.copy(pr1)
-    >>> pr_c2 = copy.copy(pr2)
-    >>> pr_c3 = copy.copy(pr3)
     >>> pr1 == pr_c1
     True
     >>> pr2 == pr_c2
@@ -433,19 +431,37 @@
     >>> id(pr3) == id(pr_c3)
     False
 
-`set` doesn't handle persistent reference objects properly. All
-following set operations produce wrong results.
-
     >>> set1 = set((pr1, pr2))
+    >>> set1
+    set([SPR (1), SPR (2)])
     >>> len(set1)
     2
     >>> set2 = set((pr_c1, pr_c3))
+    >>> set2
+    set([SPR (1), SPR (3)])
     >>> len(set2)
     2
     >>> set_c1 = set((pr_c1, pr_c2))
+    >>> set_c1
+    set([SPR (1), SPR (2)])
     >>> len(set_c1)
     2
 
+`set` doesn't handle persistent reference objects properly. All
+following set operations produce wrong results.
+
+Deduplication:
+
+    >>> set((pr1, pr_c1))
+    set([SPR (1), SPR (1)])
+    >>> set((pr2, pr_c2))
+    set([SPR (2), SPR (2)])
+    >>> set((pr1, pr_c1, pr2))
+    set([SPR (1), SPR (1), SPR (2)])
+    >>> set4 = set((pr1, pr2, pr3, pr_c1, pr_c2, pr_c3))
+    >>> len(set4)
+    6
+
 Minus operation:
 
     >>> set3 = set1 - set2
@@ -454,12 +470,6 @@
     >>> set3
     set([SPR (1), SPR (2)])
 
-Deduplication:
-
-    >>> set4 = set((pr1, pr2, pr3, pr_c1, pr_c2, pr_c3))
-    >>> len(set4)
-    6
-
 Contains:
 
     >>> pr3 in set2
@@ -475,77 +485,104 @@
     >>> set1 == set_c1
     False
 
-So we made `PersistentReferenceSet` acting as `set`.
+So we made `PersistentReferenceProxy` wrapping `PersistentReference`
+to work with sets.
 
-    >>> from zc.queue._queue import PersistentReferenceSet
-    >>> pr_set1 = PersistentReferenceSet((pr1, pr2))
-    >>> len(pr_set1)
+    >>> from zc.queue._queue import PersistentReferenceProxy
+    >>> prp1 = PersistentReferenceProxy(pr1)
+    >>> prp2 = PersistentReferenceProxy(pr2)
+    >>> prp3 = PersistentReferenceProxy(pr3)
+    >>> prp_c1 = PersistentReferenceProxy(pr_c1)
+    >>> prp_c2 = PersistentReferenceProxy(pr_c2)
+    >>> prp_c3 = PersistentReferenceProxy(pr_c3)
+    >>> prp1 == prp_c1
+    True
+    >>> prp2 == prp_c2
+    True
+    >>> prp3 == prp_c3
+    True
+    >>> id(prp1) == id(prp_c1)
+    False
+    >>> id(prp2) == id(prp_c2)
+    False
+    >>> id(prp3) == id(prp_c3)
+    False
+
+    >>> set1 = set((prp1, prp2))
+    >>> set1
+    set([SPR (1), SPR (2)])
+    >>> len(set1)
     2
-    >>> pr_set2 = PersistentReferenceSet((pr_c1, pr_c3))
-    >>> len(pr_set1)
+    >>> set2 = set((prp_c1, prp_c3))
+    >>> set2
+    set([SPR (1), SPR (3)])
+    >>> len(set2)
     2
-    >>> pr_set_c1 = PersistentReferenceSet((pr_c1, pr_c2))
-    >>> len(pr_set1)
+    >>> set_c1 = set((prp_c1, prp_c2))
+    >>> set_c1
+    set([SPR (1), SPR (2)])
+    >>> len(set_c1)
     2
 
-Minus operation:
+`set` handles persistent reference properly now. All following set
+operations produce correct results.
 
-    >>> pr_set3 = pr_set1 - pr_set2
-    >>> len(pr_set3)
-    1
-    >>> pr_set3
-    PRSet((SPR (2),))
-    >>> pr_set1 - pr_set1
-    PRSet(())
-    >>> pr_set2 - pr_set3
-    PRSet((SPR (1), SPR (3)))
-    >>> pr_set3 - pr_set2
-    PRSet((SPR (2),))
-
 Deduplication:
 
-    >>> pr_set4 = PersistentReferenceSet(
-    ...     (pr1, pr2, pr3, pr_c1, pr_c2, pr_c3))
-    >>> len(pr_set4)
+    >>> set4 = set((prp1, prp2, prp3, prp_c1, prp_c2, prp_c3))
+    >>> len(set4)
     3
-    >>> pr_set4
-    PRSet((SPR (1), SPR (2), SPR (3)))
+    >>> set((prp1, prp_c1))
+    set([SPR (1)])
+    >>> set((prp2, prp_c2))
+    set([SPR (2)])
+    >>> set((prp1, prp_c1, prp2))
+    set([SPR (1), SPR (2)])
 
+Minus operation:
+
+    >>> set3 = set1 - set2
+    >>> len(set3)
+    1
+    >>> set3
+    set([SPR (2)])
+    >>> set1 - set1
+    set([])
+    >>> set2 - set3
+    set([SPR (1), SPR (3)])
+    >>> set3 - set2
+    set([SPR (2)])
+
 Contains:
 
-    >>> pr3 in pr_set2
+    >>> prp3 in set2
     True
-    >>> pr1 in pr_set2
+    >>> prp1 in set2
     True
-    >>> pr_c1 in pr_set2
+    >>> prp_c1 in set2
     True
-    >>> pr2 not in pr_set2
+    >>> prp2 not in set2
     True
 
 Intersection:
 
-    >>> pr_set1 & pr_set2
-    PRSet((SPR (1),))
-    >>> pr_set1 & pr_set_c1
-    PRSet((SPR (1), SPR (2)))
-    >>> pr_set2 & pr_set3
-    PRSet(())
+    >>> set1 & set2
+    set([SPR (1)])
+    >>> set1 & set_c1
+    set([SPR (1), SPR (2)])
+    >>> set2 & set3
+    set([])
 
 Compare:
 
-    >>> pr_set1 == pr_set_c1
+    >>> set1 == set_c1
     True
-    >>> pr_set1 == pr_set2
-    Traceback (most recent call last):
-    ...
-    ValueError: can't reliably compare against different PersistentReferences
+    >>> set1 == set2
+    False
+    >>> set1 == set4
+    False
 
-    >>> pr_set1 == pr_set4
-    Traceback (most recent call last):
-    ...
-    ValueError: can't reliably compare against different PersistentReferences
 
-
 -----
 
 .. [#why] The queue's `pull` method is actually the interesting part in why
@@ -607,6 +644,6 @@
     unimportant.
 
 .. [#workaround] The reason why we defined
-    `PersistentReferenceSet` is that there would be a significant risk
+    `PersistentReferenceProxy` is that there would be a significant risk
     of unintended consequenses for some ZODB users if we add __hash__
     method to PersistentReference.

Modified: zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/tests.py
===================================================================
--- zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/tests.py	2012-01-10 23:32:13 UTC (rev 124024)
+++ zc.queue/branches/andrew-conflict-resolution-persistent/src/zc/queue/tests.py	2012-01-11 00:12:14 UTC (rev 124025)
@@ -170,7 +170,7 @@
     """
 
 
-class StubPersistentReference(object):
+class StubPersistentReference(ConflictResolution.PersistentReference):
     def __init__(self, oid):
         self.oid = oid
 



More information about the checkins mailing list