[Checkins] SVN: zc.relationship/trunk/ zc.relationship relies on zc.relation; zc.relationship index now subclasses zc.relation Catalog

Gary Poster gary at zope.com
Fri Aug 3 18:20:15 EDT 2007


Log message for revision 78569:
  zc.relationship relies on zc.relation; zc.relationship index now subclasses zc.relation Catalog

Changed:
  _U  zc.relationship/trunk/
  U   zc.relationship/trunk/setup.py
  U   zc.relationship/trunk/src/zc/relationship/CHANGES.txt
  U   zc.relationship/trunk/src/zc/relationship/README.txt
  U   zc.relationship/trunk/src/zc/relationship/index.py
  U   zc.relationship/trunk/src/zc/relationship/interfaces.py

-=-

Property changes on: zc.relationship/trunk
___________________________________________________________________
Name: svn:externals
   + 


Modified: zc.relationship/trunk/setup.py
===================================================================
--- zc.relationship/trunk/setup.py	2007-08-03 22:11:42 UTC (rev 78568)
+++ zc.relationship/trunk/setup.py	2007-08-03 22:20:15 UTC (rev 78569)
@@ -2,7 +2,7 @@
 
 setup(
     name="zc.relationship",
-    version="2.0dev",
+    version="2.0a1",
     packages=find_packages('src'),
     include_package_data=True,
     package_dir= {'':'src'},
@@ -28,6 +28,7 @@
         'zope.app.keyreference',
         'zope.location',
         'zope.index',
+        'zc.relation',
         
         'zope.app.testing', # TODO remove this
         'zope.app.component', # TODO remove this

Modified: zc.relationship/trunk/src/zc/relationship/CHANGES.txt
===================================================================
--- zc.relationship/trunk/src/zc/relationship/CHANGES.txt	2007-08-03 22:11:42 UTC (rev 78568)
+++ zc.relationship/trunk/src/zc/relationship/CHANGES.txt	2007-08-03 22:20:15 UTC (rev 78569)
@@ -7,10 +7,18 @@
 
 (dev version; supports Zope 3.4/Zope 2.11/ZODB 3.8)
 
+The 2.x line should be almost completely compatible with the 1.x line. 
+The one notable incompatibility does not affect the use of relationship
+containers and is small enough that it will hopefully affect noone.  If
+anyone using the 1.x line objects to the incompatibility during the
+alpha release, I will consider making it compatible (which will break
+some parallels in the API).
+
 New Requirements
 ----------------
 
 - ZODB 3.8
+- zc.relation
 
 Incompatibilities with 1.0
 --------------------------
@@ -18,12 +26,15 @@
 - `findRelationships` will now use the defaultTransitiveQueriesFactory if it
   is set.
 
-- `deactivateSets` is no longer an instantiation option (it was broken because
-  of a ZODB bug anyway, as had been described in the documentation).
+- Some instantiation exceptions have different error messages.
 
 Changes in 2.0 alpha
 --------------------
 
+- the relationship index code has been moved out to zc.relation and
+  significantly refactored there.  A fully backwards compatible subclass
+  remains in zc.relationship.index
+
 - support both 64-bit and 32-bit BTree families
 
 - support specifying indexed values by passing callables rather than
@@ -54,7 +65,7 @@
   module.  (Note that the significantly lower test coverage of the container
   code is unlikely to change without contributions: I use the index
   exclusively.  See plone.relations for a zc.relationship container with
-  very good test coverage.
+  very good test coverage.)
 
 1.1
 ===

Modified: zc.relationship/trunk/src/zc/relationship/README.txt
===================================================================
--- zc.relationship/trunk/src/zc/relationship/README.txt	2007-08-03 22:11:42 UTC (rev 78568)
+++ zc.relationship/trunk/src/zc/relationship/README.txt	2007-08-03 22:20:15 UTC (rev 78569)
@@ -26,6 +26,10 @@
 This current document describes the relationship index.  See
 container.txt for documentation of the relationship container.
 
+**PLEASE NOTE: the index in zc.relationship, described below, now exists for
+backwards compatibility.  zc.relation.catalog now contains the most recent,
+backward-incompatible version of the index code.**
+
 =====
 Index
 =====
@@ -1902,7 +1906,7 @@
     >>> ix.findRelationshipTokens({'supervisor': 'Duane'}, maxDepth=3)
     Traceback (most recent call last):
     ...
-    ValueError: if maxDepth != 1, transitiveQueriesFactory must be available
+    ValueError: if maxDepth not in (None, 1), queryFactory must be available
 
     >>> ix.defaultTransitiveQueriesFactory = factory
 
@@ -1973,7 +1977,7 @@
     ... # doctest: +ELLIPSIS
     Traceback (most recent call last):
     ...
-    ValueError: ('Duplicate in attrs', 'objects', <...Attribute ...>)
+    ValueError: ('name already used', 'objects')
 
     >>> ix = index.Index(
     ...     ({'callable': subjects, 'multiple': True, 'name': 'subjects'},
@@ -1981,10 +1985,11 @@
     ...      {'callable': subjects, 'multiple': True, 'name': 'objects'},
     ...      IContextAwareRelationship['getContext']),
     ...     index.TransposingTransitiveQueriesFactory('subjects', 'objects'))
-    ... # doctest: +ELLIPSIS
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
     Traceback (most recent call last):
     ...
-    ValueError: ('Duplicate in attrs', 'objects', <...AttrGetter ...>)
+    ValueError: ('element already indexed',
+                 <zc.relationship.README.AttrGetter object at ...>)
 
     >>> ix = index.Index(
     ...     ({'element': IRelationship['objects'], 'multiple': True,
@@ -1993,16 +1998,17 @@
     ...      {'element': IRelationship['objects'], 'multiple': True},
     ...      IContextAwareRelationship['getContext']),
     ...     index.TransposingTransitiveQueriesFactory('subjects', 'objects'))
-    ... # doctest: +ELLIPSIS
+    ... # doctest: +ELLIPSIS +NORMALIZE_WHITESPACE
     Traceback (most recent call last):
     ...
-    ValueError: ('Duplicate in attrs', 'objects', <...Attribute ...>)
+    ValueError: ('element already indexed',
+                 <zope.interface.interface.Attribute object at ...>)
 
 .. [#neither_or_both] It is not allowed to provide only one or the other of
     'load' and 'dump'.
 
     >>> ix = index.Index(
-    ...     ({'element': IRelationship['objects'], 'multiple': True,
+    ...     ({'element': IRelationship['subjects'], 'multiple': True,
     ...       'name': 'subjects','dump': None},
     ...      IRelationship['relationshiptype'],
     ...      {'element': IRelationship['objects'], 'multiple': True},

Modified: zc.relationship/trunk/src/zc/relationship/index.py
===================================================================
--- zc.relationship/trunk/src/zc/relationship/index.py	2007-08-03 22:11:42 UTC (rev 78568)
+++ zc.relationship/trunk/src/zc/relationship/index.py	2007-08-03 22:20:15 UTC (rev 78569)
@@ -13,20 +13,19 @@
 
 from zc.relationship import interfaces
 
+import zc.relation.catalog
+
+# N.B.
+# this is now a subclass of the zc.relation.catalog.Catalog.  It only exists
+# to provide backwards compatibility.  New work should go in zc.relation.
+# Ideally, new code should use the zc.relation code directly.
+
 ##############################################################################
 # the marker that shows that a path is circular
 #
 
-class CircularRelationshipPath(tuple):
-    interface.implements(interfaces.ICircularRelationshipPath)
+CircularRelationshipPath = zc.relation.catalog.CircularRelationPath
 
-    def __new__(kls, elements, cycled):
-        res = super(CircularRelationshipPath, kls).__new__(kls, elements)
-        res.cycled = cycled
-        return res
-    def __repr__(self):
-        return 'cycle%s' % super(CircularRelationshipPath, self).__repr__()
-
 ##############################################################################
 # a common case transitive queries factory
 
@@ -67,6 +66,14 @@
                 res.update(static)
                 yield res
 
+def factoryWrapper(factory, query, index):
+    cache = {}
+    def getQueries(relchain):
+        if not relchain:
+            return (query,)
+        return factory(relchain, query, index, cache)
+    return getQueries
+
 ##############################################################################
 # a common case intid getter and setter
 
@@ -91,255 +98,72 @@
         ('BTree', 'TreeSet', 'Bucket', 'Set',
          'intersection', 'multiunion', 'union', 'difference'))
 
-class Index(persistent.Persistent, zope.app.container.contained.Contained):
-    interface.implements(interfaces.IIndex)
+class Index(zc.relation.catalog.Catalog,
+            zope.app.container.contained.Contained):
+    interface.implementsOnly(
+        interfaces.IIndex, interface.implementedBy(persistent.Persistent),
+        interface.implementedBy(zope.app.container.contained.Contained))
 
-    family = BTrees.family32
-
     def __init__(self, attrs, defaultTransitiveQueriesFactory=None,
                  dumpRel=generateToken, loadRel=resolveToken,
-                 relFamily=None, family=None):
-        if family is not None:
-            self.family = family
-        else:
-            family = self.family
-        self._name_TO_mapping = family.OO.BTree()
-        # held mappings are objtoken to (relcount, relset)
-        self._EMPTY_name_TO_relcount_relset = family.OO.BTree()
-        self._reltoken_name_TO_objtokenset = family.OO.BTree()
+                 relFamily=None, family=None, deactivateSets=False):
+        super(Index, self).__init__(dumpRel, loadRel, relFamily, family)
         self.defaultTransitiveQueriesFactory = defaultTransitiveQueriesFactory
-        if relFamily is None:
-            relFamily = family.IF
-        self._relTools = getModuleTools(relFamily)
-        self._relTools['load'] = loadRel
-        self._relTools['dump'] = dumpRel
-        self._relLength = Length.Length()
-        self._relTokens = self._relTools['TreeSet']()
-        self._attrs = _attrs = {} # this is private, and it's not expected to
-        # mutate after this initial setting.
-        seen = set()
         for data in attrs:
-            # see README.txt for description of attrs.
-
             if zope.interface.interfaces.IElement.providedBy(data):
                 data = {'element': data}
-            res = getModuleTools(data.get('btree', family.IF))
-            res['dump'] = data.get('dump', generateToken)
-            res['load'] = data.get('load', resolveToken)
-            res['multiple'] = data.get('multiple', False)
-            if (res['dump'] is None) ^ (res['load'] is None):
-                raise ValueError(
-                    "either both of 'dump' and 'load' must be None, or "
-                    "neither")
-                # when both load and dump are None, this is a small
-                # optimization that can be a large optimization if the returned
-                # value is one of the main four options of the selected btree
-                # family (BTree, TreeSet, Set, Bucket).
-
-            if 'element' in data:
-                if 'callable' in data:
+            if 'callable' in data:
+                if 'element' in data:
                     raise ValueError(
                         'cannot provide both callable and element')
-                res['element'] = val = data['element']
-                name = res['attrname'] = val.__name__
-                res['interface'] = val.interface
-                res['call'] = zope.interface.interfaces.IMethod.providedBy(val)
-            elif 'callable' not in data:
+                data['element'] = data.pop('callable')
+            elif 'element' not in data:
                 raise ValueError('must provide element or callable')
-            else:
-                # must return iterable or None
-                val = res['callable'] = data['callable']
-                name = getattr(res['callable'], '__name__', None)
-            res['name'] = data.get('name', name)
-            if res['name'] is None:
-                raise ValueError('no name specified')
-            if res['name'] in _attrs or val in seen:
-                raise ValueError('Duplicate in attrs', res['name'], val)
-            seen.add(val)
-            if res['TreeSet'].__name__[0] == 'I':
-                Mapping = BTrees.family32.IO.BTree
-            elif res['TreeSet'].__name__[0] == 'L':
-                Mapping = BTrees.family64.IO.BTree
-            else:
-                assert res['TreeSet'].__name__.startswith('O')
-                Mapping = family.OO.BTree
-            self._name_TO_mapping[res['name']] = Mapping()
-            # these are objtoken to (relcount, relset)
-            _attrs[res['name']] = res
-        
+            if 'dump' not in data:
+                data['dump'] = generateToken
+            if 'load' not in data:
+                data['load'] = resolveToken
+            self.addValueIndex(**data)
+        # deactivateSets is now ignored.  It was broken before.
 
-    def _getValuesAndTokens(self, rel, data):
-        values = None
-        if 'interface' in data:
-            valueSource = data['interface'](rel, None)
-            if valueSource is not None:
-                values = getattr(valueSource, data['attrname'])
-                if data['call']:
-                    values = values()
-        else:
-            values = data['callable'](rel, self)
-        if not data['multiple'] and values is not None:
-            # None is a marker for no value
-            values = (values,)
-        optimization = data['dump'] is None and (
-            values is None or 
-            isinstance(values, (
-                data['TreeSet'], data['BTree'], data['Bucket'], data['Set'])))
-        if not values:
-            return None, None, optimization
-        elif optimization:
-            # this is the optimization story (see _add)
-            return values, values, optimization
-        else:
-            cache = {}
-            if data['dump'] is None:
-                tokens = data['TreeSet'](values)
-            else:
-                tokens = data['TreeSet'](
-                    data['dump'](o, self, cache) for o in values)
-            return values, tokens, False
+    # disable zc.relation default query factories, enable zc.relationship
+    addDefaultQueryFactory = iterDefaultQueryFactories = None
+    removeDefaultQueryFactory = None
+    def _getQueryFactory(self, query, queryFactory):
+        res = None
+        if queryFactory is None:
+            queryFactory = self.defaultTransitiveQueriesFactory
+        if queryFactory is not None:
+            res = factoryWrapper(queryFactory, query, self)
+        return queryFactory, res
 
-    def _add(self, relToken, tokens, name, fullTokens):
-        self._reltoken_name_TO_objtokenset[(relToken, name)] = fullTokens
-        if tokens is None:
-            dataset = self._EMPTY_name_TO_relcount_relset
-            keys = (name,)
-        else:
-            dataset = self._name_TO_mapping[name]
-            keys = tokens
-        for key in keys:
-            data = dataset.get(key)
-            if data is None:
-                data = dataset[key] = (
-                    Length.Length(), self._relTools['TreeSet']())
-            res = data[1].insert(relToken)
-            assert res, 'Internal error: relToken existed in data'
-            data[0].change(1)
+    # disable search indexes
+    _iterListeners = zc.relation.catalog.Catalog.iterListeners
+    def _getSearchIndexResults(self, name, query, maxDepth, filter,
+                               targetQuery, targetFilter, queryFactory):
+        return None
+    addSearchIndex = iterSearchIndexes = removeSearchIndex = None
 
-    def _remove(self, relToken, tokens, name):
-        if tokens is None:
-            dataset = self._EMPTY_name_TO_relcount_relset
-            keys = (name,)
-        else:
-            dataset = self._name_TO_mapping[name]
-            keys = tokens
-        for key in keys:
-            data = dataset[key]
-            data[1].remove(relToken)
-            data[0].change(-1)
-            if not data[0].value:
-                del dataset[key]
-            else:
-                assert data[0].value > 0
-
-    def index(self, rel):
-        self.index_doc(self._relTools['dump'](rel, self, {}), rel)
-
-    def index_doc(self, relToken, rel):
-        if relToken in self._relTokens:
-            # reindex
-            for data in self._attrs.values():
-                values, newTokens, optimization = self._getValuesAndTokens(
-                    rel, data)
-                oldTokens = self._reltoken_name_TO_objtokenset[
-                    (relToken, data['name'])]
-                if newTokens != oldTokens:
-                    if newTokens is not None and oldTokens is not None:
-                        added = data['difference'](newTokens, oldTokens)
-                        removed = data['difference'](oldTokens, newTokens)
-                        if optimization:
-                            # the goal of this optimization is to not have to
-                            # recreate a TreeSet (which can be large and
-                            # relatively timeconsuming) when only small changes
-                            # have been made.  We ballpark this by saying
-                            # "if there are only a few removals, do them, and
-                            # then do an update: it's almost certainly a win
-                            # over essentially generating a new TreeSet and
-                            # updating it with *all* values.  On the other
-                            # hand, if there are a lot of removals, it's
-                            # probably quicker just to make a new one."  See
-                            # timeit/set_creation_vs_removal.py for details.
-                            # A len is pretty cheap--all the buckets should
-                            # already be in memory.
-                            len_removed = len(removed)
-                            if len_removed < 5:
-                                recycle = True
-                            else:
-                                len_old = len(oldTokens)
-                                ratio = float(len_old)/len_removed
-                                recycle = (ratio <= 0.1 or len_old > 500
-                                           and ratio < 0.2)
-                            if recycle:
-                                for t in removed:
-                                    oldTokens.remove(t)
-                                oldTokens.update(added)
-                                newTokens = oldTokens
-                            else:
-                                newTokens = data['TreeSet'](newTokens)
-                    else:
-                        removed = oldTokens
-                        added = newTokens
-                        if optimization and newTokens is not None:
-                            newTokens = data['TreeSet'](newTokens)
-                    self._remove(relToken, removed, data['name'])
-                    self._add(relToken, added, data['name'], newTokens)
-        else:
-            # new
-            for data in self._attrs.values():
-                assert self._reltoken_name_TO_objtokenset.get(
-                    (relToken, data['name']), self) is self
-                values, tokens, optimization = self._getValuesAndTokens(
-                    rel, data)
-                if optimization and tokens is not None:
-                    tokens = data['TreeSet'](tokens)
-                self._add(relToken, tokens, data['name'], tokens)
-            self._relTokens.insert(relToken)
-            self._relLength.change(1)
-
-    def unindex(self, rel):
-        self.unindex_doc(self._relTools['dump'](rel, self, {}))
-
-    def __contains__(self, rel):
-        return self.tokenizeRelationship(rel) in self._relTokens   
-
-    def unindex_doc(self, relToken):
-        if relToken in self._relTokens:
-            for data in self._attrs.values():
-                tokens = self._reltoken_name_TO_objtokenset.pop(
-                    (relToken, data['name']))
-                self._remove(relToken, tokens, data['name'])
-            self._relTokens.remove(relToken)
-            self._relLength.change(-1)
-
     def documentCount(self):
         return self._relLength.value
 
     def wordCount(self):
-        return 0 # we don't index words; we could arbitrarily keep track of
-        # how many related objects we have, but that would be annoying to get
-        # right for very questionable benefit
+        return 0 # we don't index words
 
-    def clear(self):
-        for v in self._name_TO_mapping.values():
-            v.clear()
-        self._EMPTY_name_TO_relcount_relset.clear()
-        self._reltoken_name_TO_objtokenset.clear()
-        self._relTokens.clear()
-        self._relLength.set(0)
-
     def apply(self, query):
         # there are two kinds of queries: values and relationships.
         if len(query) != 1:
             raise ValueError('one key in the primary query dictionary')
         (searchType, query) = query.items()[0]
         if searchType=='relationships':
-            if self._relTools['TreeSet'].__name__[:2] not in ('IF', 'LF'):
+            relTools = self.getRelationModuleTools()
+            if relTools['TreeSet'].__name__[:2] not in ('IF', 'LF'):
                 raise ValueError(
                     'cannot fulfill `apply` interface because cannot return '
                     'an (I|L)FBTree-based result')
-            res = self._relData(query)
+            res = self.getRelationTokens(query)
             if res is None:
-                res = self._relTools['TreeSet']()
+                res = relTools['TreeSet']()
             return res
         elif searchType=='values':
             data = self._attrs[query['resultName']]
@@ -347,354 +171,110 @@
                 raise ValueError(
                     'cannot fulfill `apply` interface because cannot return '
                     'an (I|L)FBTree-based result')
+            q = BTrees.family32.OO.Bucket(query.get('query', ()))
+            targetq = BTrees.family32.OO.Bucket(query.get('targetQuery', ()))
+            queryFactory, getQueries = self._getQueryFactory(
+                q, query.get('transitiveQueriesFactory'))
             iterable = self._yieldValueTokens(
                 query['resultName'], *(self._parse(
-                    query['query'], query.get('maxDepth'),
-                    query.get('filter'), query.get('targetQuery'),
-                    query.get('targetFilter'),
-                    query.get('transitiveQueriesFactory')) +
+                    q, query.get('maxDepth'), query.get('filter'), targetq,
+                    query.get('targetFilter'), getQueries) +
                 (True,)))
             # IF and LF have multiunion; can demand its presence
             return data['multiunion'](tuple(iterable))
         else:
             raise ValueError('unknown query type', searchType)
 
-    def tokenizeQuery(self, query):
-        res = {}
-        if getattr(query, 'items', None) is not None:
-            query = query.items()
-        for k, v in query:
-            if k is None:
-                v = self._relTools['dump'](v, self, {})
-            else:
-                data = self._attrs[k]
-                if v is not None and data['dump'] is not None:
-                    v = data['dump'](v, self, {})
-            res[k] = v
-        return res
+    tokenizeRelationship = zc.relation.catalog.Catalog.tokenizeRelation
 
-    def resolveQuery(self, query):
-        res = {}
-        if getattr(query, 'items', None) is not None:
-            query = query.items()
-        for k, v in query:
-            if k is None:
-                v = self._relTools['load'](v, self, {})
-            else:
-                data = self._attrs[k]
-                if v is not None and data['load'] is not None:
-                    v = data['load'](v, self, {})
-            res[k] = v
-        return res
+    resolveRelationshipToken = (
+        zc.relation.catalog.Catalog.resolveRelationToken)
 
-    def tokenizeValues(self, values, name):
-        dump = self._attrs[name]['dump']
-        if dump is None:
-            return values
-        cache = {}
-        return (dump(v, self, cache) for v in values)
+    tokenizeRelationships = zc.relation.catalog.Catalog.tokenizeRelations
 
-    def resolveValueTokens(self, tokens, name):
-        load = self._attrs[name]['load']
-        if load is None:
-            return tokens
-        cache = {}
-        return (load(t, self, cache) for t in tokens)
+    resolveRelationshipTokens = (
+        zc.relation.catalog.Catalog.resolveRelationTokens)
 
-    def tokenizeRelationship(self, rel):
-        return self._relTools['dump'](rel, self, {})
-
-    def resolveRelationshipToken(self, token):
-        return self._relTools['load'](token, self, {})
-
-    def tokenizeRelationships(self, rels):
-        cache = {}
-        return (self._relTools['dump'](r, self, cache) for r in rels)
-
-    def resolveRelationshipTokens(self, tokens):
-        cache = {}
-        return (self._relTools['load'](t, self, cache) for t in tokens)
-
     def findRelationshipTokenSet(self, query):
-        # equivalent to, and used by, non-transitive
-        # findRelationshipTokens(query)
+        # equivalent to findRelationshipTokens(query, maxDepth=1)
         res = self._relData(query)
         if res is None:
             res = self._relTools['TreeSet']()
         return res
 
     def findValueTokenSet(self, reltoken, name):
-        # equivalent to, and used by, non-transitive
-        # findValueTokens(name, {None: reltoken})
+        # equivalent to findValueTokens(name, {None: reltoken}, maxDepth=1)
         res = self._reltoken_name_TO_objtokenset.get((reltoken, name))
         if res is None:
             res = self._attrs[name]['TreeSet']()
         return res
 
-    def _relData(self, searchTerms):
-        data = []
-        if getattr(searchTerms, 'items', None) is not None: # quack
-            searchTerms = searchTerms.items()
-        searchTerms = tuple(searchTerms)
-        if not searchTerms:
-            return self._relTokens
-        rel = None
-        for nm, token in searchTerms:
-            if nm is None:
-                rel = token
-                if rel not in self._relTokens:
-                    return None
-            else:
-                if token is None:
-                    relData = self._EMPTY_name_TO_relcount_relset.get(nm)
-                else:
-                    relData = self._name_TO_mapping[nm].get(token)
-                if relData is None or relData[0].value == 0:
-                    return None
-                data.append((relData[0].value, relData[1])) # length, set
-        # we don't want to sort on the set values!! just the lengths.
-        data.sort(key=lambda i: i[0])
-        if rel is not None:
-            for l, s in data:
-                if rel not in s:
-                    return None
-            return self._relTools['TreeSet']((rel,))
-        # we had an untested optimization attempt here before.  It has
-        # been tested now (see timeit/manual_intersection.py), found
-        # useless, and removed.
-        #
-        # we know we have at least one result now.  intersect all.  Work
-        # from smallest to largest, until we're done or we don't have any
-        # more results.
-        res = data.pop(0)[1]
-        while res and data:
-            res = self._relTools['intersection'](res, data.pop(0)[1])
-        return res
-
-    def _parse(self, query, maxDepth, filter, targetQuery, targetFilter,
-               transitiveQueriesFactory):
-        relData = self._relData(query)
-        if maxDepth is not None and (
-            not isinstance(maxDepth, (int, long)) or maxDepth < 1):
-            raise ValueError('maxDepth must be None or a positive integer')
-        if filter is not None:
-            filterCache = {}
-            def checkFilter(relchain, query):
-                return filter(relchain, query, self, filterCache)
-        else:
-            checkFilter = None
-        targetCache = {}
-        checkTargetFilter = None
-        if targetQuery is not None:
-            targetData = self._relData(targetQuery)
-            if targetData is None:
-                relData = None # shortcut
-            else:
-                if targetFilter is not None:
-                    def checkTargetFilter(relchain, query):
-                        return relchain[-1] in targetData and targetFilter(
-                            relchain, query, self, targetCache)
-                else:
-                    def checkTargetFilter(relchain, query):
-                        return relchain[-1] in targetData
-        elif targetFilter is not None:
-            def checkTargetFilter(relchain, query):
-                return targetFilter(relchain, query, self, targetCache)
-        getQueries = None
-        if transitiveQueriesFactory is None:
-            transitiveQueriesFactory = self.defaultTransitiveQueriesFactory
-        if transitiveQueriesFactory is None:
-            if maxDepth != 1 and maxDepth is not None:
-                raise ValueError(
-                    'if maxDepth != 1, transitiveQueriesFactory must be '
-                    'available')
-        else:
-            transitiveCache = {}
-            def getQueries(relchain, query):
-                return transitiveQueriesFactory(
-                    relchain, query, self, transitiveCache)
-        return (query, relData, maxDepth, checkFilter, checkTargetFilter,
-                getQueries)
-
     def findValueTokens(self, resultName, query=(), maxDepth=None,
                         filter=None, targetQuery=None, targetFilter=None,
                         transitiveQueriesFactory=None):
-        data = self._attrs.get(resultName)
-        if data is None:
-            raise ValueError('name not indexed', resultName)
-        if (((maxDepth is None and transitiveQueriesFactory is None and
-              self.defaultTransitiveQueriesFactory is None)
-             or maxDepth==1)
-            and filter is None and not targetQuery and targetFilter is None):
-            if not query:
-                return self._name_TO_mapping[resultName]
-            rels = self._relData(query)
-            if not rels:
-                return data['TreeSet']()
-            elif len(rels) == 1:
-                return self.findValueTokenSet(iter(rels).next(), resultName)
-            else:
-                iterable = (
-                    self._reltoken_name_TO_objtokenset.get((r, resultName))
-                    for r in rels)
-                if data['multiunion'] is not None:
-                    res = data['multiunion'](tuple(iterable))
-                else:
-                    res = data['TreeSet']()
-                    for s in iterable:
-                        res = data['union'](res, s)
-                return res
-        return self._yieldValueTokens(
-            resultName, *self._parse(
-                query, maxDepth, filter, targetQuery, targetFilter,
-                transitiveQueriesFactory))
+        # argument names changed slightly
+        if targetQuery is None:
+            targetQuery = ()
+        return super(Index, self).findValueTokens(
+            resultName, query, maxDepth, filter, targetQuery, targetFilter,
+            transitiveQueriesFactory)
 
     def findValues(self, resultName, query=(), maxDepth=None, filter=None,
                    targetQuery=None, targetFilter=None,
                    transitiveQueriesFactory=None):
-        data = self._attrs.get(resultName)
-        if data is None:
-            raise ValueError('name not indexed', resultName)
-        resolve = data['load']
-        res = self.findValueTokens(resultName, query, maxDepth, filter,
-                                   targetQuery, targetFilter,
-                                   transitiveQueriesFactory)
-        if resolve is None:
-            return res
-        else:
-            cache = {}
-            return (resolve(t, self, cache) for t in res)
+        # argument names changed slightly
+        if targetQuery is None:
+            targetQuery = ()
+        return super(Index, self).findValues(
+            resultName, query, maxDepth, filter, targetQuery, targetFilter,
+            transitiveQueriesFactory)
 
-    def _yieldValueTokens(
-        self, resultName, query, relData, maxDepth, checkFilter,
-        checkTargetFilter, getQueries, yieldSets=False):
-        relSeen = set()
-        objSeen = set()
-        for path in self._yieldRelationshipTokenChains(
-            query, relData, maxDepth, checkFilter, checkTargetFilter,
-            getQueries, findCycles=False):
-            relToken = path[-1]
-            if relToken not in relSeen:
-                relSeen.add(relToken)
-                outputSet = self._reltoken_name_TO_objtokenset.get(
-                    (relToken, resultName))
-                if outputSet:
-                    if yieldSets:
-                        yield outputSet
-                    else:
-                        for token in outputSet:
-                            if token not in objSeen:
-                                yield token
-                                objSeen.add(token)
-
-    def findRelationshipTokens(self, query=(), maxDepth=None, filter=None,
-                               targetQuery=None, targetFilter=None,
-                               transitiveQueriesFactory=None):
-        if (((maxDepth is None and transitiveQueriesFactory is None and
-              self.defaultTransitiveQueriesFactory is None)
-             or maxDepth==1)
-            and filter is None and not targetQuery and targetFilter is None):
-            return self.findRelationshipTokenSet(query)
-        seen = self._relTools['TreeSet']()
-        return (res[-1] for res in self._yieldRelationshipTokenChains(
-                    *self._parse(query, maxDepth, filter, targetQuery,
-                                 targetFilter, transitiveQueriesFactory) +
-                    (False,))
-                if seen.insert(res[-1]))
-
     def findRelationships(self, query=(), maxDepth=None, filter=None,
                           targetQuery=None, targetFilter=None,
                           transitiveQueriesFactory=None):
-        return self.resolveRelationshipTokens(
-            self.findRelationshipTokens(
-                query, maxDepth, filter, targetQuery, targetFilter,
-                transitiveQueriesFactory))
+        # argument names changed slightly
+        if targetQuery is None:
+            targetQuery = ()
+        return super(Index, self).findRelations(
+            query, maxDepth, filter, targetQuery, targetFilter,
+            transitiveQueriesFactory)
 
-    def findRelationshipChains(self, query, maxDepth=None, filter=None,
+    def findRelationshipTokens(self, query=(), maxDepth=None, filter=None,
                                targetQuery=None, targetFilter=None,
                                transitiveQueriesFactory=None):
-        """find relationship tokens that match the searchTerms.
-        
-        same arguments as findValueTokens except no resultName.
-        """
-        return self._yieldRelationshipChains(*self._parse(
+        # argument names changed slightly
+        if targetQuery is None:
+            targetQuery = ()
+        return super(Index, self).findRelationTokens(
             query, maxDepth, filter, targetQuery, targetFilter,
-            transitiveQueriesFactory))
+            transitiveQueriesFactory)
 
-    def _yieldRelationshipChains(self, query, relData, maxDepth, checkFilter,
-                                 checkTargetFilter, getQueries,
-                                 findCycles=True):
-        resolve = self._relTools['load']
-        cache = {}
-        for p in self._yieldRelationshipTokenChains(
-            query, relData, maxDepth, checkFilter, checkTargetFilter,
-            getQueries, findCycles):
-            t = (resolve(t, self, cache) for t in p)
-            if interfaces.ICircularRelationshipPath.providedBy(p):
-                res = CircularRelationshipPath(t, p.cycled)
-            else:
-                res = tuple(t)
-            yield res
-
-    def findRelationshipTokenChains(self, query, maxDepth=None, filter=None,
+    def findRelationshipTokenChains(self, query=(), maxDepth=None, filter=None,
                                     targetQuery=None, targetFilter=None,
                                     transitiveQueriesFactory=None):
-        """find relationship tokens that match the searchTerms.
-        
-        same arguments as findValueTokens except no resultName.
-        """
-        return self._yieldRelationshipTokenChains(*self._parse(
+        # argument names changed slightly
+        if targetQuery is None:
+            targetQuery = ()
+        return super(Index, self).findRelationTokenChains(
             query, maxDepth, filter, targetQuery, targetFilter,
-            transitiveQueriesFactory))
+            transitiveQueriesFactory)
 
-    def _yieldRelationshipTokenChains(self, query, relData, maxDepth,
-                                      checkFilter, checkTargetFilter,
-                                      getQueries, findCycles=True):
-        if not relData:
-            raise StopIteration
-        stack = [((), iter(relData))]
-        while stack:
-            tokenChain, relDataIter = stack[0]
-            try:
-                relToken = relDataIter.next()
-            except StopIteration:
-                stack.pop(0)
-            else:
-                tokenChain += (relToken,)
-                if checkFilter is not None and not checkFilter(
-                    tokenChain, query):
-                    continue
-                walkFurther = maxDepth is None or len(tokenChain) < maxDepth
-                if getQueries is not None and (walkFurther or findCycles):
-                    oldInputs = frozenset(tokenChain)
-                    next = set()
-                    cycled = []
-                    for q in getQueries(tokenChain, query):
-                        relData = self._relData(q)
-                        if relData:
-                            intersection = oldInputs.intersection(relData)
-                            if intersection:
-                                # it's a cycle
-                                cycled.append(q)
-                            elif walkFurther:
-                                next.update(relData)
-                    if walkFurther and next:
-                        stack.append((tokenChain, iter(next)))
-                    if cycled:
-                        tokenChain = CircularRelationshipPath(
-                            tokenChain, cycled)
-                if (checkTargetFilter is None or
-                    checkTargetFilter(tokenChain, query)):
-                    yield tokenChain
+    def findRelationshipChains(self, query=(), maxDepth=None, filter=None,
+                               targetQuery=None, targetFilter=None,
+                               transitiveQueriesFactory=None):
+        # argument names changed slightly
+        if targetQuery is None:
+            targetQuery = ()
+        return super(Index, self).findRelationChains(
+            query, maxDepth, filter, targetQuery, targetFilter,
+            transitiveQueriesFactory)
 
-    def isLinked(self, query, maxDepth=None, filter=None,
+    def isLinked(self, query=(), maxDepth=None, filter=None,
                  targetQuery=None, targetFilter=None,
                  transitiveQueriesFactory=None):
-        try:
-            self._yieldRelationshipTokenChains(*self._parse(
-                query, maxDepth, filter, targetQuery, targetFilter,
-                transitiveQueriesFactory)+(False,)).next()
-        except StopIteration:
-            return False
-        else:
-            return True
+        # argument names changed slightly
+        if targetQuery is None:
+            targetQuery = ()
+        return super(Index, self).canFind(
+            query, maxDepth, filter, targetQuery, targetFilter,
+            transitiveQueriesFactory)

Modified: zc.relationship/trunk/src/zc/relationship/interfaces.py
===================================================================
--- zc.relationship/trunk/src/zc/relationship/interfaces.py	2007-08-03 22:11:42 UTC (rev 78568)
+++ zc.relationship/trunk/src/zc/relationship/interfaces.py	2007-08-03 22:20:15 UTC (rev 78569)
@@ -18,14 +18,10 @@
 from zope import interface
 from zope.app.container.interfaces import IReadContainer
 import zope.index.interfaces
+import zc.relation.interfaces
 
-class ICircularRelationshipPath(interface.Interface):
-    """A tuple that has a circular relationship in the very final element of
-    the path."""
+ICircularRelationshipPath = zc.relation.interfaces.ICircularRelationPath
 
-    cycled = interface.Attribute(
-        """a list of the searches needed to continue the cycle""")
-
 class ITransitiveQueriesFactory(interface.Interface):
     def __call__(relchain, query, index, cache):
         """return iterable of queries to search further from given relchain.



More information about the Checkins mailing list