[CalendarServer-changes] [11909] CalendarServer/trunk/twext/who

source_changes at macosforge.org source_changes at macosforge.org
Wed Mar 12 11:20:19 PDT 2014


Revision: 11909
          http://trac.calendarserver.org//changeset/11909
Author:   wsanchez at apple.com
Date:     2013-11-07 18:14:21 -0800 (Thu, 07 Nov 2013)
Log Message:
-----------
Replace recordsFromQuery() with recordsFromExpression() that can handle a CompoundExpression.

Modified Paths:
--------------
    CalendarServer/trunk/twext/who/aggregate.py
    CalendarServer/trunk/twext/who/directory.py
    CalendarServer/trunk/twext/who/idirectory.py
    CalendarServer/trunk/twext/who/index.py
    CalendarServer/trunk/twext/who/test/test_directory.py
    CalendarServer/trunk/twext/who/test/test_xml.py

Modified: CalendarServer/trunk/twext/who/aggregate.py
===================================================================
--- CalendarServer/trunk/twext/who/aggregate.py	2013-11-08 00:36:07 UTC (rev 11908)
+++ CalendarServer/trunk/twext/who/aggregate.py	2013-11-08 02:14:21 UTC (rev 11909)
@@ -78,10 +78,10 @@
         return self._recordType
 
 
-    def recordsFromExpression(self, expression, records=None):
+    def recordsFromExpression(self, expression):
         ds = []
         for service in self.services:
-            d = service.recordsFromExpression(expression, records)
+            d = service.recordsFromExpression(expression)
             ds.append(d)
 
         def unwrapFirstError(f):

Modified: CalendarServer/trunk/twext/who/directory.py
===================================================================
--- CalendarServer/trunk/twext/who/directory.py	2013-11-08 00:36:07 UTC (rev 11908)
+++ CalendarServer/trunk/twext/who/directory.py	2013-11-08 02:14:21 UTC (rev 11909)
@@ -33,7 +33,7 @@
 from twext.who.idirectory import FieldName, RecordType
 from twext.who.idirectory import Operand
 from twext.who.idirectory import IDirectoryService, IDirectoryRecord
-from twext.who.expression import MatchExpression
+from twext.who.expression import CompoundExpression, MatchExpression
 from twext.who.util import uniqueResult, describe
 
 
@@ -43,25 +43,30 @@
     """
     Generic implementation of L{IDirectoryService}.
 
-    This is a complete implementation of L{IDirectoryService}, with support for
-    the query operands in L{Operand}.
+    Most of the C{recordsWith*} methods call L{recordsWithFieldValue}, which in
+    turn calls L{recordsFromExpression} with a corresponding
+    L{MatchExpression}.
 
-    The C{recordsWith*} methods are all implemented in terms of
-    L{recordsWithFieldValue}, which is in turn implemented in terms of
-    L{recordsFromExpression}.
-    L{recordsFromQuery} is also implemented in terms of
-    {recordsFromExpression}.
+    L{recordsFromExpression} relies on L{recordsFromNonCompoundExpression} for
+    all expression types other than L{CompoundExpression}, which it handles
+    directly.
 
-    L{recordsFromExpression} (and therefore most uses of the other methods)
-    will always fail with a L{QueryNotSupportedError}.
+    L{recordsFromNonCompoundExpression} (and therefore most uses of the other
+    methods) will always fail with a L{QueryNotSupportedError}.
 
-    A subclass should therefore override L{recordsFromExpression} with an
-    implementation that handles any queries that it can support and its
-    superclass' implementation with any query it cannot support.
+    A subclass should therefore override L{recordsFromNonCompoundExpression}
+    with an implementation that handles any queries that it can support (which
+    should include L{MatchExpression}) and calls its superclass' implementation
+    with any query it cannot support.
 
-    A subclass may override L{recordsFromQuery} if it is to support additional
-    operands.
+    A subclass may override L{recordsFromExpression} if it is to support
+    L{CompoundExpression}s with operands other than L{Operand.AND} and
+    L{Operand.OR}.
 
+    A subclass may override L{recordsFromExpression} if it is built on top
+    of a directory service that supports compound expressions, as that may be
+    more effient than relying on L{DirectoryService}'s implementation.
+
     L{updateRecords} and L{removeRecords} will fail with L{NotAllowedError}
     when asked to modify data.
     A subclass should override these methods if is to allow editing of
@@ -108,20 +113,19 @@
         return self.recordType.iterconstants()
 
 
-    def recordsFromExpression(self, expression, records=None):
+    def recordsFromNonCompoundExpression(self, expression, records=None):
         """
-        Finds records matching a single expression.
+        Finds records matching a expression.
 
-        @note: The implementation in L{DirectoryService} always raises
-            L{QueryNotSupportedError}.
+        @note: This method is called by L{recordsFromExpression} to handle
+            all expressions other than L{CompoundExpression}.
+            This implementation always fails with L{QueryNotSupportedError}.
+            Subclasses should override this in order to handle additional
+            expression types, and call on the superclass' implementation
+            for other expression types.
 
-        @note: This L{DirectoryService} adds a C{records} keyword argument to
-            the interface defined by L{IDirectoryService}.
-            This allows the implementation of
-            L{DirectoryService.recordsFromQuery} to narrow the scope of records
-            being searched as it applies expressions.
-            This is therefore relevant to subclasses, which need to support the
-            added parameter, but not to users of L{IDirectoryService}.
+        @note: This interface is the same as L{recordsFromExpression}, except
+            for the additional C{records} argument.
 
         @param expression: an expression to apply
         @type expression: L{object}
@@ -142,44 +146,55 @@
 
 
     @inlineCallbacks
-    def recordsFromQuery(self, expressions, operand=Operand.AND):
-        expressionIterator = iter(expressions)
+    def recordsFromExpression(self, expression):
+        if isinstance(expression, CompoundExpression):
+            operand = expression.operand
+            subExpressions = iter(expression.expressions)
 
-        try:
-            expression = expressionIterator.next()
-        except StopIteration:
-            returnValue(())
+            try:
+                subExpression = subExpressions.next()
+            except StopIteration:
+                returnValue(())
 
-        results = set((yield self.recordsFromExpression(expression)))
+            results = set((
+                yield self.recordsFromNonCompoundExpression(subExpression)
+            ))
 
-        for expression in expressions:
-            if operand == Operand.AND:
-                if not results:
-                    # No need to bother continuing here
-                    returnValue(())
+            for subExpression in subExpressions:
+                if operand == Operand.AND:
+                    if not results:
+                        # No need to bother continuing here
+                        returnValue(())
 
-                records = results
-            else:
-                records = None
+                    records = results
+                else:
+                    records = None
 
-            recordsMatchingExpression = frozenset((
-                yield self.recordsFromExpression(expression, records=records)
-            ))
+                recordsMatchingExpression = frozenset((
+                    yield self.recordsFromNonCompoundExpression(
+                        subExpression,
+                        records=records
+                    )
+                ))
 
-            if operand == Operand.AND:
-                results &= recordsMatchingExpression
-            elif operand == Operand.OR:
-                results |= recordsMatchingExpression
-            else:
-                raise QueryNotSupportedError(
-                    "Unknown operand: {0}".format(operand)
-                )
+                if operand == Operand.AND:
+                    results &= recordsMatchingExpression
+                elif operand == Operand.OR:
+                    results |= recordsMatchingExpression
+                else:
+                    raise QueryNotSupportedError(
+                        "Unknown operand: {0}".format(operand)
+                    )
+        else:
+            results = yield self.recordsFromNonCompoundExpression(expression)
 
         returnValue(results)
 
 
     def recordsWithFieldValue(self, fieldName, value):
-        return self.recordsFromExpression(MatchExpression(fieldName, value))
+        return self.recordsFromExpression(
+            MatchExpression(fieldName, value)
+        )
 
 
     @inlineCallbacks
@@ -202,10 +217,17 @@
 
     @inlineCallbacks
     def recordWithShortName(self, recordType, shortName):
-        returnValue(uniqueResult((yield self.recordsFromQuery((
-            MatchExpression(FieldName.recordType, recordType),
-            MatchExpression(FieldName.shortNames, shortName),
-        )))))
+        returnValue(uniqueResult((
+            yield self.recordsFromExpression(
+                CompoundExpression(
+                    (
+                        MatchExpression(FieldName.recordType, recordType),
+                        MatchExpression(FieldName.shortNames, shortName),
+                    ),
+                    operand=Operand.AND
+                )
+            )
+        )))
 
 
     def recordsWithEmailAddress(self, emailAddress):

Modified: CalendarServer/trunk/twext/who/idirectory.py
===================================================================
--- CalendarServer/trunk/twext/who/idirectory.py	2013-11-08 00:36:07 UTC (rev 11908)
+++ CalendarServer/trunk/twext/who/idirectory.py	2013-11-08 02:14:21 UTC (rev 11909)
@@ -249,7 +249,7 @@
         """
 
 
-    def recordsFromExpression(self, expression):
+    def recordsFromExpression(expression):
         """
         Find records matching an expression.
 
@@ -264,25 +264,6 @@
         """
 
 
-    def recordsFromQuery(expressions, operand=Operand.AND):
-        """
-        Find records by composing a query consisting of an iterable of
-        expressions and an operand.
-
-        @param expressions: expressions to query against
-        @type expressions: iterable of L{object}s
-
-        @param operand: an operand
-        @type operand: a L{NamedConstant}
-
-        @return: The matching records.
-        @rtype: deferred iterable of L{IDirectoryRecord}s
-
-        @raises: L{QueryNotSupportedError} if the query is not
-            supported by this directory service.
-        """
-
-
     def recordsWithFieldValue(fieldName, value):
         """
         Find records that have the given field name with the given

Modified: CalendarServer/trunk/twext/who/index.py
===================================================================
--- CalendarServer/trunk/twext/who/index.py	2013-11-08 00:36:07 UTC (rev 11908)
+++ CalendarServer/trunk/twext/who/index.py	2013-11-08 02:14:21 UTC (rev 11909)
@@ -215,7 +215,7 @@
         return succeed(result)
 
 
-    def recordsFromExpression(self, expression, records=None):
+    def recordsFromNonCompoundExpression(self, expression, records=None):
         if isinstance(expression, MatchExpression):
             if expression.fieldName in self.indexedFields:
                 return self.indexedRecordsFromMatchExpression(
@@ -226,7 +226,7 @@
                     expression, records=records
                 )
         else:
-            return BaseDirectoryService.recordsFromExpression(
+            return BaseDirectoryService.recordsFromNonCompoundExpression(
                 self, expression, records=records
             )
 

Modified: CalendarServer/trunk/twext/who/test/test_directory.py
===================================================================
--- CalendarServer/trunk/twext/who/test/test_directory.py	2013-11-08 00:36:07 UTC (rev 11908)
+++ CalendarServer/trunk/twext/who/test/test_directory.py	2013-11-08 02:14:21 UTC (rev 11909)
@@ -27,10 +27,10 @@
 from twext.who.idirectory import QueryNotSupportedError, NotAllowedError
 from twext.who.idirectory import RecordType, FieldName
 from twext.who.idirectory import IDirectoryService, IDirectoryRecord
+from twext.who.expression import CompoundExpression
 from twext.who.directory import DirectoryService, DirectoryRecord
 
 
-
 class ServiceMixIn(object):
     realmName = u"xyzzy"
 
@@ -69,20 +69,20 @@
         )
 
 
-    @inlineCallbacks
-    def test_recordsFromQueryNone(self):
-        service = self.service()
-        records = (yield service.recordsFromQuery(()))
-        for record in records:
-            self.failTest("No records expected")
+    # @inlineCallbacks
+    # def test_recordsFromExpressionNone(self):
+    #     service = self.service()
+    #     records = (yield service.recordsFromExpression(CompoundExpression()))
+    #     for record in records:
+    #         self.failTest("No records expected")
 
 
-    def test_recordsFromQueryBogus(self):
-        service = self.service()
-        self.assertFailure(
-            service.recordsFromQuery((object(),)),
-            QueryNotSupportedError
-        )
+    # def test_recordsFromExpressionBogus(self):
+    #     service = self.service()
+    #     self.assertFailure(
+    #         service.recordsFromQuery(CompoundExpression(object(),)),
+    #         QueryNotSupportedError
+    #     )
 
 
     def test_recordWithUID(self):

Modified: CalendarServer/trunk/twext/who/test/test_xml.py
===================================================================
--- CalendarServer/trunk/twext/who/test/test_xml.py	2013-11-08 00:36:07 UTC (rev 11908)
+++ CalendarServer/trunk/twext/who/test/test_xml.py	2013-11-08 02:14:21 UTC (rev 11909)
@@ -27,6 +27,7 @@
 
 from twext.who.idirectory import NoSuchRecordError
 from twext.who.idirectory import Operand
+from twext.who.expression import CompoundExpression
 from twext.who.expression import MatchExpression, MatchType, MatchFlags
 from twext.who.xml import ParseError
 from twext.who.xml import DirectoryService, DirectoryRecord
@@ -300,12 +301,14 @@
     @inlineCallbacks
     def test_queryAnd(self):
         service = self.service()
-        records = yield service.recordsFromQuery(
-            (
-                service.query(u"emailAddresses", u"shared at example.com"),
-                service.query(u"shortNames", u"sagen"),
-            ),
-            operand=Operand.AND
+        records = yield service.recordsFromExpression(
+            CompoundExpression(
+                (
+                    service.query(u"emailAddresses", u"shared at example.com"),
+                    service.query(u"shortNames", u"sagen"),
+                ),
+                operand=Operand.AND
+            )
         )
         self.assertRecords(records, (u"__sagen__",))
 
@@ -316,12 +319,14 @@
         Test optimized case, where first expression yields no results.
         """
         service = self.service()
-        records = yield service.recordsFromQuery(
-            (
-                service.query(u"emailAddresses", u"nobody at example.com"),
-                service.query(u"shortNames", u"sagen"),
-            ),
-            operand=Operand.AND
+        records = yield service.recordsFromExpression(
+            CompoundExpression(
+                (
+                    service.query(u"emailAddresses", u"nobody at example.com"),
+                    service.query(u"shortNames", u"sagen"),
+                ),
+                operand=Operand.AND
+            )
         )
         self.assertRecords(records, ())
 
@@ -329,25 +334,37 @@
     @inlineCallbacks
     def test_queryOr(self):
         service = self.service()
-        records = yield service.recordsFromQuery(
-            (
-                service.query(u"emailAddresses", u"shared at example.com"),
-                service.query(u"shortNames", u"wsanchez"),
-            ),
-            operand=Operand.OR
+        records = yield service.recordsFromExpression(
+            CompoundExpression(
+                (
+                    service.query(u"emailAddresses", u"shared at example.com"),
+                    service.query(u"shortNames", u"wsanchez"),
+                ),
+                operand=Operand.OR
+            )
         )
-        self.assertRecords(records, (u"__sagen__", u"__dre__", u"__wsanchez__"))
+        self.assertRecords(
+            records,
+            (u"__sagen__", u"__dre__", u"__wsanchez__")
+        )
 
 
     @inlineCallbacks
     def test_queryNot(self):
         service = self.service()
-        records = yield service.recordsFromQuery(
-            (
-                service.query(u"emailAddresses", u"shared at example.com"),
-                service.query(u"shortNames", u"sagen", flags=MatchFlags.NOT),
-            ),
-            operand=Operand.AND
+        records = yield service.recordsFromExpression(
+            CompoundExpression(
+                (
+                    service.query(
+                        u"emailAddresses", u"shared at example.com"
+                    ),
+                    service.query(
+                        u"shortNames", u"sagen",
+                        flags=MatchFlags.NOT
+                    ),
+                ),
+                operand=Operand.AND
+            )
         )
         self.assertRecords(records, (u"__dre__",))
 
@@ -355,15 +372,17 @@
     @inlineCallbacks
     def test_queryNotNoIndex(self):
         service = self.service()
-        records = yield service.recordsFromQuery(
-            (
-                service.query(u"emailAddresses", u"shared at example.com"),
-                service.query(
-                    u"fullNames", u"Andre LaBranche",
-                    flags=MatchFlags.NOT
+        records = yield service.recordsFromExpression(
+            CompoundExpression(
+                (
+                    service.query(u"emailAddresses", u"shared at example.com"),
+                    service.query(
+                        u"fullNames", u"Andre LaBranche",
+                        flags=MatchFlags.NOT
+                    ),
                 ),
-            ),
-            operand=Operand.AND
+                operand=Operand.AND
+            )
         )
         self.assertRecords(records, (u"__sagen__",))
 
@@ -371,58 +390,61 @@
     @inlineCallbacks
     def test_queryCaseInsensitive(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"shortNames", u"SagEn",
                 flags=MatchFlags.caseInsensitive
-            ),
-        ))
+            )
+        )
         self.assertRecords(records, (u"__sagen__",))
 
 
     @inlineCallbacks
     def test_queryCaseInsensitiveNoIndex(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"fullNames", u"moRGen SAGen",
                 flags=MatchFlags.caseInsensitive
-            ),
-        ))
+            )
+        )
         self.assertRecords(records, (u"__sagen__",))
 
 
     @inlineCallbacks
     def test_queryStartsWith(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
-            service.query(u"shortNames", u"wil", matchType=MatchType.startsWith),
-        ))
+        records = yield service.recordsFromExpression(
+            service.query(
+                u"shortNames", u"wil",
+                matchType=MatchType.startsWith
+            )
+        )
         self.assertRecords(records, (u"__wsanchez__",))
 
 
     @inlineCallbacks
     def test_queryStartsWithNoIndex(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"fullNames", u"Wilfredo",
                 matchType=MatchType.startsWith
-            ),
-        ))
+            )
+        )
         self.assertRecords(records, (u"__wsanchez__",))
 
 
     @inlineCallbacks
     def test_queryStartsWithNot(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"shortNames", u"w",
                 matchType=MatchType.startsWith,
                 flags=MatchFlags.NOT,
-            ),
-        ))
+            )
+        )
         self.assertRecords(
             records,
             (
@@ -450,13 +472,13 @@
         should NOT require that all match?
         """
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"shortNames", u"wil",
                 matchType=MatchType.startsWith,
                 flags=MatchFlags.NOT,
-            ),
-        ))
+            )
+        )
         self.assertRecords(
             records,
             (
@@ -479,13 +501,13 @@
     @inlineCallbacks
     def test_queryStartsWithNotNoIndex(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"fullNames", u"Wilfredo",
                 matchType=MatchType.startsWith,
                 flags=MatchFlags.NOT,
-            ),
-        ))
+            )
+        )
         self.assertRecords(
             records,
             (
@@ -507,60 +529,63 @@
     @inlineCallbacks
     def test_queryStartsWithCaseInsensitive(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"shortNames", u"WIL",
                 matchType=MatchType.startsWith,
                 flags=MatchFlags.caseInsensitive,
-            ),
-        ))
+            )
+        )
         self.assertRecords(records, (u"__wsanchez__",))
 
 
     @inlineCallbacks
     def test_queryStartsWithCaseInsensitiveNoIndex(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"fullNames", u"wilfrEdo",
                 matchType=MatchType.startsWith,
                 flags=MatchFlags.caseInsensitive,
-            ),
-        ))
+            )
+        )
         self.assertRecords(records, (u"__wsanchez__",))
 
 
     @inlineCallbacks
     def test_queryContains(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"shortNames", u"sanchez",
-                matchType=MatchType.contains
-            ),
-        ))
+                matchType=MatchType.contains,
+            )
+        )
         self.assertRecords(records, (u"__wsanchez__",))
 
 
     @inlineCallbacks
     def test_queryContainsNoIndex(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
-            service.query(u"fullNames", u"fred", matchType=MatchType.contains),
-        ))
+        records = yield service.recordsFromExpression(
+            service.query(
+                u"fullNames", u"fred",
+                matchType=MatchType.contains,
+            )
+        )
         self.assertRecords(records, (u"__wsanchez__",))
 
 
     @inlineCallbacks
     def test_queryContainsNot(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"shortNames", u"sanchez",
                 matchType=MatchType.contains,
                 flags=MatchFlags.NOT,
-            ),
-        ))
+            )
+        )
         self.assertRecords(
             records,
             (
@@ -582,13 +607,13 @@
     @inlineCallbacks
     def test_queryContainsNotNoIndex(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"fullNames", u"fred",
                 matchType=MatchType.contains,
                 flags=MatchFlags.NOT,
-            ),
-        ))
+            )
+        )
         self.assertRecords(
             records,
             (
@@ -610,26 +635,26 @@
     @inlineCallbacks
     def test_queryContainsCaseInsensitive(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"shortNames", u"Sanchez",
                 matchType=MatchType.contains,
                 flags=MatchFlags.caseInsensitive,
-            ),
-        ))
+            )
+        )
         self.assertRecords(records, (u"__wsanchez__",))
 
 
     @inlineCallbacks
     def test_queryContainsCaseInsensitiveNoIndex(self):
         service = self.service()
-        records = yield service.recordsFromQuery((
+        records = yield service.recordsFromExpression(
             service.query(
                 u"fullNames", u"frEdo",
                 matchType=MatchType.contains,
                 flags=MatchFlags.caseInsensitive,
-            ),
-        ))
+            )
+        )
         self.assertRecords(records, (u"__wsanchez__",))
 
 
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140312/9a03fe96/attachment.html>


More information about the calendarserver-changes mailing list