Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 2 commits
  • 4 files changed
  • 0 commit comments
  • 1 contributor
Commits on Apr 10, 2012
@mcdonc mcdonc comment 499092b
Commits on Apr 11, 2012
@mcdonc mcdonc coverage, remove set_oid db5196e
Showing with 266 additions and 70 deletions.
  1. +80 −53 substanced/objectmap/__init__.py
  2. +167 −14 substanced/objectmap/tests.py
  3. +0 −3 substanced/util/__init__.py
  4. +19 −0 substanced/util/tests.py
View
133 substanced/objectmap/__init__.py
@@ -242,9 +242,8 @@ def remove(self, obj_objectid_or_path_tuple):
if not oidset2:
del omap2[i]
- for refset in self.referencemap.refmap.values():
- refset.remove(removed)
-
+ self.referencemap.remove(removed)
+
return removed
def _get_path_tuple(self, obj_or_path_tuple):
@@ -314,14 +313,36 @@ def pathlookup(self, obj_or_path_tuple, depth=None, include_origin=True):
return result
+ def _refids_for(self, source, target):
+ sourceid, targetid = oid_of(source, source), oid_of(target, target)
+ if not sourceid in self.objectid_to_path:
+ raise ValueError('source %s is not in objectmap' % (source,))
+ if not targetid in self.objectid_to_path:
+ raise ValueError('target %s is not in objectmap' % (target,))
+ return sourceid, targetid
+
+ def _refid_for(self, obj):
+ oid = oid_of(obj, obj)
+ if not oid in self.objectid_to_path:
+ raise ValueError('oid %s is not in objectmap' % (obj,))
+ return oid
+
def connect(self, source, target, reftype):
- source, target = oid_of(source, source), oid_of(target, target)
- self.referencemap.connect(source, target, reftype)
+ sourceid, targetid = self._refids_for(source, target)
+ self.referencemap.connect(sourceid, targetid, reftype)
def disconnect(self, source, target, reftype):
- source, target = oid_of(source, source), oid_of(target, target)
+ sourceid, targetid = self._refids_for(source, target)
self.referencemap.disconnect(source, target, reftype)
+ def sourceids(self, obj, reftype):
+ oid = self._refid_for(obj)
+ return self.referencemap.sourceids(oid, reftype)
+
+ def targetids(self, obj, reftype):
+ oid = self._refid_for(obj)
+ return self.referencemap.targetids(oid, reftype)
+
def sources(self, obj, reftype):
for oid in self.sourceids(obj, reftype):
yield self.object_for(oid)
@@ -330,13 +351,39 @@ def targets(self, obj, reftype):
for oid in self.targetids(obj, reftype):
yield self.object_for(oid)
- def sourceids(self, obj, reftype):
- oid = oid_of(obj, obj)
- return self.referencemap.sourceids(oid, reftype)
+class ReferenceMap(Persistent):
+
+ family = BTrees.family32
+
+ def __init__(self, refmap=None):
+ if refmap is None:
+ refmap = self.family.OO.BTree()
+ self.refmap = refmap
- def targetids(self, obj, reftype):
- oid = oid_of(obj, obj)
- return self.referencemap.targetids(oid, reftype)
+ def connect(self, source, target, reftype):
+ refset = self.refmap.setdefault(reftype, ReferenceSet())
+ refset.connect(source, target)
+
+ def disconnect(self, source, target, reftype):
+ refset = self.refmap.get(reftype)
+ if refset is not None:
+ refset.disconnect(source, target)
+
+ def targetids(self, oid, reftype):
+ refset = self.refmap.get(reftype)
+ if refset is not None:
+ return refset.targetids(oid)
+ return self.family.IF.Set()
+
+ def sourceids(self, oid, reftype):
+ refset = self.refmap.get(reftype)
+ if refset is not None:
+ return refset.sourceids(oid)
+ return self.family.IF.Set()
+
+ def remove(self, oids):
+ for refset in self.refmap.values():
+ refset.remove(oids)
class ReferenceSet(Persistent):
@@ -352,18 +399,6 @@ def connect(self, source, target):
sources = self.target2src.setdefault(target, self.family.IF.TreeSet())
sources.insert(source)
- def remove(self, oidset):
- # if it's the target
- for oid in oidset:
- if oid in self.src2target:
- targets = self.src2target.pop(oid)
- for target in targets:
- self.target2src.get(target).remove(oid)
- if oid in self.target2src:
- sources = self.target2src.pop(oid)
- for source in sources:
- self.src2target.get(source).remove(oid)
-
def disconnect(self, source, target):
targets = self.src2target.get(source)
if targets is not None:
@@ -385,36 +420,28 @@ def targetids(self, oid):
def sourceids(self, oid):
return self.target2src.get(oid, self.family.IF.Set())
-class ReferenceMap(Persistent):
-
- family = BTrees.family32
+ def remove(self, oidset):
+ # XXX is there a way to make this less expensive?
+ removed = self.family.IF.Set()
+ for oid in oidset:
+ if oid in self.src2target:
+ removed.insert(oid)
+ targets = self.src2target.pop(oid)
+ for target in targets:
+ oidset = self.target2src.get(target)
+ oidset.remove(oid)
+ if not oidset:
+ del self.target2src[target]
+ if oid in self.target2src:
+ removed.insert(oid)
+ sources = self.target2src.pop(oid)
+ for source in sources:
+ oidset = self.src2target.get(source)
+ oidset.remove(oid)
+ if not oidset:
+ del self.src2target[source]
+ return removed
- def __init__(self, refmap=None):
- if refmap is None:
- refmap = self.family.OO.BTree()
- self.refmap = refmap
-
- def connect(self, source, target, reftype):
- refset = self.refmap.setdefault(reftype, ReferenceSet())
- refset.connect(source, target)
-
- def disconnect(self, source, target, reftype):
- refset = self.refmap.get(reftype)
- if refset is not None:
- refset.disconnect(source, target)
-
- def targetids(self, oid, reftype):
- refset = self.refmap.get(reftype)
- if refset is not None:
- return refset.targetids(oid)
- return self.family.IF.Set()
-
- def sourceids(self, oid, reftype):
- refset = self.refmap.get(reftype)
- if refset is not None:
- return refset.sourceids(oid)
- return self.family.IF.Set()
-
def node_path_tuple(resource):
# cant use resource_path_tuple from pyramid, it wants everything to
# have a __name__
View
181 substanced/objectmap/tests.py
@@ -423,6 +423,102 @@ def l(path, depth=None, include_origin=True):
assert dict(objmap.objectid_to_path) == {}
assert dict(objmap.path_to_objectid) == {}
+ def test__refids_for_source_missing(self):
+ inst = self._makeOne()
+ self.assertRaises(ValueError, inst._refids_for, 1, 2)
+
+ def test__refids_for_target_missing(self):
+ inst = self._makeOne()
+ inst.objectid_to_path[1] = (u'',)
+ self.assertRaises(ValueError, inst._refids_for, 1, 2)
+
+ def test__refids_for_success_oids(self):
+ inst = self._makeOne()
+ inst.objectid_to_path[1] = (u'',)
+ inst.objectid_to_path[2] = (u'',)
+ s, t = inst._refids_for(1, 2)
+ self.assertEqual(s, 1)
+ self.assertEqual(t, 2)
+
+ def test__refids_for_success_objects(self):
+ inst = self._makeOne()
+ one = testing.DummyResource()
+ one.__objectid__ = 1
+ two = testing.DummyResource()
+ two.__objectid__ = 2
+ inst.objectid_to_path[1] = (u'',)
+ inst.objectid_to_path[2] = (u'',)
+ s, t = inst._refids_for(one, two)
+ self.assertEqual(s, 1)
+ self.assertEqual(t, 2)
+
+ def test__refid_for_missing(self):
+ inst = self._makeOne()
+ self.assertRaises(ValueError, inst._refid_for, 1)
+
+ def test__refid_for_success_oid(self):
+ inst = self._makeOne()
+ inst.objectid_to_path[1] = (u'',)
+ oid = inst._refid_for(1)
+ self.assertEqual(oid, 1)
+
+ def test__refid_for_success_object(self):
+ inst = self._makeOne()
+ obj = testing.DummyResource()
+ obj.__objectid__ = 1
+ inst.objectid_to_path[1] = (u'',)
+ oid = inst._refid_for(obj)
+ self.assertEqual(oid, 1)
+
+ def test_connect(self):
+ inst = self._makeOne()
+ inst.objectid_to_path[1] = (u'',)
+ inst.objectid_to_path[2] = (u'', u'a')
+ inst.referencemap = DummyReferenceMap()
+ inst.connect(1, 2, 'ref')
+ self.assertEqual(inst.referencemap['ref'], (1, 2))
+
+ def test_disconnect(self):
+ inst = self._makeOne()
+ inst.objectid_to_path[1] = (u'',)
+ inst.objectid_to_path[2] = (u'', u'a')
+ inst.referencemap = DummyReferenceMap()
+ inst.referencemap['ref'] = True
+ inst.disconnect(1, 2, 'ref')
+ self.assertTrue('ref' not in inst.referencemap)
+
+ def test_sourceids(self):
+ inst = self._makeOne()
+ inst.objectid_to_path[1] = (u'',)
+ inst.referencemap = DummyReferenceMap(sourceids=[2])
+ self.assertEqual(inst.sourceids(1, 'ref'), [2])
+
+ def test_targetids(self):
+ inst = self._makeOne()
+ inst.objectid_to_path[1] = (u'',)
+ inst.referencemap = DummyReferenceMap(targetids=[2])
+ self.assertEqual(inst.targetids(1, 'ref'), [2])
+
+ def test_sources(self):
+ inst = self._makeOne()
+ inst.objectid_to_path[1] = (u'',)
+ inst.objectid_to_path[2] = (u'', u'a')
+ inst.objectid_to_path[3] = (u'', u'b')
+ inst.referencemap = DummyReferenceMap(sourceids=[2, 3])
+ obj = object()
+ inst._find_resource = lambda *arg: obj
+ self.assertEqual(list(inst.sources(1, 'ref')), [obj, obj])
+
+ def test_targets(self):
+ inst = self._makeOne()
+ inst.objectid_to_path[1] = (u'',)
+ inst.objectid_to_path[2] = (u'', u'a')
+ inst.objectid_to_path[3] = (u'', u'b')
+ inst.referencemap = DummyReferenceMap(targetids=[2, 3])
+ obj = object()
+ inst._find_resource = lambda *arg: obj
+ self.assertEqual(list(inst.targets(1, 'ref')), [obj, obj])
+
class TestReferenceSet(unittest.TestCase):
def _makeOne(self):
from . import ReferenceSet
@@ -436,16 +532,16 @@ def test_connect_empty(self):
def test_connect_nonempty_overlap(self):
refset = self._makeOne()
- refset.src2target[1] = DummyOOTreeSet([2])
- refset.target2src[2] = DummyOOTreeSet([1])
+ refset.src2target[1] = DummyTreeSet([2])
+ refset.target2src[2] = DummyTreeSet([1])
refset.connect(1, 2)
self.assertEqual(list(refset.src2target[1]), [2])
self.assertEqual(list(refset.target2src[2]), [1])
def test_connect_nonempty_nonoverlap(self):
refset = self._makeOne()
- refset.src2target[1] = DummyOOTreeSet([3])
- refset.target2src[2] = DummyOOTreeSet([4])
+ refset.src2target[1] = DummyTreeSet([3])
+ refset.target2src[2] = DummyTreeSet([4])
refset.connect(1, 2)
self.assertEqual(sorted(list(refset.src2target[1])), [2, 3])
self.assertEqual(sorted(list(refset.target2src[2])), [1, 4])
@@ -458,32 +554,62 @@ def test_disconnect_empty(self):
def test_disconnect_nonempty(self):
refset = self._makeOne()
- refset.src2target[1] = DummyOOTreeSet([2, 3])
- refset.target2src[2] = DummyOOTreeSet([1, 4])
+ refset.src2target[1] = DummyTreeSet([2, 3])
+ refset.target2src[2] = DummyTreeSet([1, 4])
refset.disconnect(1, 2)
self.assertEqual(list(refset.src2target[1]), [3])
self.assertEqual(list(refset.target2src[2]), [4])
def test_disconnect_keyerrors(self):
refset = self._makeOne()
- refset.src2target[1] = DummyOOTreeSet()
- refset.target2src[2] = DummyOOTreeSet()
+ refset.src2target[1] = DummyTreeSet()
+ refset.target2src[2] = DummyTreeSet()
refset.disconnect(1, 2)
self.assertEqual(list(refset.src2target[1]), [])
self.assertEqual(list(refset.target2src[2]), [])
def test_targetids(self):
refset = self._makeOne()
- dummyset = DummyOOTreeSet([1])
+ dummyset = DummyTreeSet([1])
refset.src2target[1] = dummyset
self.assertEqual(refset.targetids(1), dummyset)
def test_sourceids(self):
refset = self._makeOne()
- dummyset = DummyOOTreeSet(['a'])
+ dummyset = DummyTreeSet([1])
refset.target2src[1] = dummyset
self.assertEqual(refset.sourceids(1), dummyset)
+ def test_remove_empty(self):
+ refset = self._makeOne()
+ self.assertEqual(list(refset.remove([1,2])), [])
+
+ def test_remove_notempty(self):
+ # emulate the result of:
+ # connect(1, 2)
+ # connect(3, 4)
+ # connect(2, 4)
+ # connect(2, 1)
+ # connect(3, 5)
+ src2target = {1:DummyTreeSet([2]), 3:DummyTreeSet([4, 5]),
+ 2:DummyTreeSet([4, 1])}
+ target2src = {2:DummyTreeSet([1]), 4:DummyTreeSet([3, 2]),
+ 1:DummyTreeSet([2]), 5:DummyTreeSet([3])}
+ refset = self._makeOne()
+ refset.src2target = src2target
+ refset.target2src = target2src
+ oids = [1,4, 10]
+ result = refset.remove(oids)
+ self.assertEqual(list(result), [1,4]) # 10 unmentioned
+ self.assertEqual(
+ src2target,
+ {3:DummyTreeSet([5])}
+ )
+ self.assertEqual(
+ target2src,
+ {5:DummyTreeSet([3])}
+ )
+
class TestReferenceMap(unittest.TestCase):
def _makeOne(self, map=None):
from . import ReferenceMap
@@ -527,6 +653,17 @@ def test_sourceids_with_refset(self):
refs = self._makeOne(map)
self.assertEqual(list(refs.sourceids('a', 'reftype')), ['123'])
+ def test_remove(self):
+ L = []
+ refset1 = DummyReferenceSet()
+ refset2 = DummyReferenceSet()
+ refset1.remove = lambda oids: L.append(oids)
+ refset2.remove = lambda oids: L.append(oids)
+ map = {'reftype':refset1, 'reftype2':refset2}
+ refs = self._makeOne(map)
+ refs.remove([1,2])
+ self.assertEqual(L, [[1,2], [1,2]])
+
class Test_object_will_be_added(unittest.TestCase):
def _callFUT(self, object, event):
from . import object_will_be_added
@@ -656,11 +793,11 @@ class DummyEvent(object):
def __init__(self, parent):
self.parent = parent
-class DummyOOTreeSet(set):
+class DummyTreeSet(set):
def insert(self, val):
self.add(val)
-
-class DummyReferenceSet:
+
+class DummyReferenceSet(object):
def __init__(self, result=True):
self.result = result
self.connected = []
@@ -677,8 +814,24 @@ def targetids(self, src):
def sourceids(self, src):
return self.result
-
+
+class DummyReferenceMap(dict):
+ def __init__(self, sourceids=(), targetids=()):
+ self._sourceids = sourceids
+ self._targetids = targetids
+ def connect(self, src, target, reftype):
+ self[reftype] = (src, target)
+
+ def disconnect(self, src, target, reftype):
+ del self[reftype]
+
+ def sourceids(self, oid, reftype):
+ return self._sourceids
+
+ def targetids(self, oid, reftype):
+ return self._targetids
+
def _makeSite(**kw):
from ..interfaces import IFolder
site = testing.DummyResource(__provides__=kw.pop('__provides__', None))
View
3 substanced/util/__init__.py
@@ -31,6 +31,3 @@ def oid_of(obj, default=_marker):
raise
return default
-def set_oid(obj, value):
- obj.__objectid__ = value
-
View
19 substanced/util/tests.py
@@ -46,3 +46,22 @@ def test_IFolder_node_folder_children(self):
result = list(self._callFUT(model))
self.assertEqual(result, [four, three, two, one, model])
+from . import _marker
+
+class Test_oid_of(unittest.TestCase):
+ def _callFUT(self, obj, default=_marker):
+ from . import oid_of
+ return oid_of(obj, _marker)
+
+ def test_gardenpath(self):
+ obj = testing.DummyResource()
+ obj.__objectid__ = 1
+ self.assertEqual(self._callFUT(obj), 1)
+
+ def test_no_objectid_no_default(self):
+ obj = testing.DummyResource()
+ self.assertRaises(AttributeError, self._callFUT, obj)
+
+ def test_no_objectid_with_default(self):
+ obj = testing.DummyResource()
+ self.assertEqual(self._callFUT(obj, 1), 1)

No commit comments for this range

Something went wrong with that request. Please try again.