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

source_changes at macosforge.org source_changes at macosforge.org
Fri Feb 15 15:25:56 PST 2013


Revision: 10747
          http://trac.calendarserver.org//changeset/10747
Author:   wsanchez at apple.com
Date:     2013-02-15 15:25:56 -0800 (Fri, 15 Feb 2013)
Log Message:
-----------
Move index logic into it's own set of directory classes and have XML inherit from that.

Modified Paths:
--------------
    CalendarServer/trunk/twext/who/xml.py

Added Paths:
-----------
    CalendarServer/trunk/twext/who/index.py

Copied: CalendarServer/trunk/twext/who/index.py (from rev 10746, CalendarServer/trunk/twext/who/xml.py)
===================================================================
--- CalendarServer/trunk/twext/who/index.py	                        (rev 0)
+++ CalendarServer/trunk/twext/who/index.py	2013-02-15 23:25:56 UTC (rev 10747)
@@ -0,0 +1,216 @@
+##
+# Copyright (c) 2006-2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+"""
+Indexed directory service implementation.
+"""
+
+__all__ = [
+    "DirectoryService",
+    "DirectoryRecord",
+]
+
+from twisted.python.constants import Names, NamedConstant
+from twisted.internet.defer import succeed, inlineCallbacks, returnValue
+
+from twext.who.util import MergedConstants, describe, uniqueResult, iterFlags
+from twext.who.idirectory import FieldName as BaseFieldName
+from twext.who.idirectory import MatchType, QueryFlags
+from twext.who.idirectory import DirectoryQueryMatchExpression
+from twext.who.directory import DirectoryService as BaseDirectoryService
+from twext.who.directory import DirectoryRecord as BaseDirectoryRecord
+
+
+
+##
+# Data type extentions
+##
+
+class FieldName(Names):
+    memberUIDs = NamedConstant()
+    memberUIDs.description = "member UIDs"
+    memberUIDs.multiValue = True
+
+
+
+##
+# Directory Service
+##
+
+class DirectoryService(BaseDirectoryService):
+    """
+    XML directory service.
+    """
+
+    fieldName = MergedConstants(BaseDirectoryService.fieldName, FieldName)
+
+    indexedFields = (
+        BaseFieldName.recordType,
+        BaseFieldName.uid,
+        BaseFieldName.guid,
+        BaseFieldName.shortNames,
+        BaseFieldName.emailAddresses,
+        FieldName.memberUIDs,
+    )
+
+
+    def __init__(self, realmName):
+        BaseDirectoryService.__init__(self, realmName)
+
+        self.flush()
+
+
+    @property
+    def index(self):
+        self.loadRecords()
+        return self._index
+
+
+    @index.setter
+    def index(self, value):
+        self._index = value
+
+
+    def loadRecords(self):
+        """
+        Load records.
+        """
+        raise NotImplementedError("Subclasses should implement loadRecords().")
+
+
+    def flush(self):
+        """
+        Flush the index.
+        """
+        self._index = None
+
+
+    @staticmethod
+    def _queryFlags(flags):
+        predicate = lambda x: x
+        normalize = lambda x: x
+
+        if flags is not None:
+            for flag in iterFlags(flags):
+                if flag == QueryFlags.NOT:
+                    predicate = lambda x: not x
+                elif flag == QueryFlags.caseInsensitive:
+                    normalize = lambda x: x.lower()
+                else:
+                    raise NotImplementedError("Unknown query flag: %s" % (describe(flag),))
+
+        return predicate, normalize
+
+
+    def indexedRecordsFromMatchExpression(self, expression, records=None):
+        """
+        Finds records in the internal indexes matching a single
+        expression.
+        @param expression: an expression
+        @type expression: L{object}
+        """
+        predicate, normalize = self._queryFlags(expression.flags)
+
+        fieldIndex = self.index[expression.fieldName]
+        matchValue = normalize(expression.fieldValue)
+        matchType  = expression.matchType
+
+        if matchType == MatchType.startsWith:
+            indexKeys = (key for key in fieldIndex if predicate(normalize(key).startswith(matchValue)))
+        elif matchType == MatchType.contains:
+            indexKeys = (key for key in fieldIndex if predicate(matchValue in normalize(key)))
+        elif matchType == MatchType.equals:
+            if predicate(True):
+                indexKeys = (matchValue,)
+            else:
+                indexKeys = (key for key in fieldIndex if normalize(key) != matchValue)
+        else:
+            raise NotImplementedError("Unknown match type: %s" % (describe(matchType),))
+
+        matchingRecords = set()
+        for key in indexKeys:
+            matchingRecords |= fieldIndex.get(key, frozenset())
+
+        if records is not None:
+            matchingRecords &= records
+
+        return succeed(matchingRecords)
+
+
+    def unIndexedRecordsFromMatchExpression(self, expression, records=None):
+        """
+        Finds records not in the internal indexes matching a single
+        expression.
+        @param expression: an expression
+        @type expression: L{object}
+        """
+        predicate, normalize = self._queryFlags(expression.flags)
+
+        matchValue = normalize(expression.fieldValue)
+        matchType  = expression.matchType
+
+        if matchType == MatchType.startsWith:
+            match = lambda fieldValue: predicate(fieldValue.startswith(matchValue))
+        elif matchType == MatchType.contains:
+            match = lambda fieldValue: predicate(matchValue in fieldValue)
+        elif matchType == MatchType.equals:
+            match = lambda fieldValue: predicate(fieldValue == matchValue)
+        else:
+            raise NotImplementedError("Unknown match type: %s" % (describe(matchType),))
+
+        result = set()
+
+        if records is None:
+            records = (uniqueResult(values) for values in self.index[self.fieldName.uid].itervalues())
+
+        for record in records:
+            fieldValues = record.fields.get(expression.fieldName, None)
+
+            if fieldValues is None:
+                continue
+
+            for fieldValue in fieldValues:
+                if match(normalize(fieldValue)):
+                    result.add(record)
+
+        return result
+
+
+    def recordsFromExpression(self, expression, records=None):
+        if isinstance(expression, DirectoryQueryMatchExpression):
+            if expression.fieldName in self.indexedFields:
+                return self.indexedRecordsFromMatchExpression(expression, records=records)
+            else:
+                return self.unIndexedRecordsFromMatchExpression(expression, records=records)
+        else:
+            return BaseDirectoryService.recordsFromExpression(self, expression, records=records)
+
+
+
+class DirectoryRecord(BaseDirectoryRecord):
+    """
+    XML directory record
+    """
+    @inlineCallbacks
+    def members(self):
+        members = set()
+        for uid in getattr(self, "memberUIDs", ()):
+            members.add((yield self.service.recordWithUID(uid)))
+        returnValue(members)
+
+
+    def groups(self):
+        return self.service.recordsWithFieldValue(FieldName.memberUIDs, self.uid)

Modified: CalendarServer/trunk/twext/who/xml.py
===================================================================
--- CalendarServer/trunk/twext/who/xml.py	2013-02-15 22:49:55 UTC (rev 10746)
+++ CalendarServer/trunk/twext/who/xml.py	2013-02-15 23:25:56 UTC (rev 10747)
@@ -33,17 +33,15 @@
 from xml.etree.ElementTree import tostring as etreeToString
 from xml.etree.ElementTree import Element as XMLElement
 
-from twisted.python.constants import Names, NamedConstant, Values, ValueConstant
-from twisted.internet.defer import succeed, fail, inlineCallbacks, returnValue
+from twisted.python.constants import Values, ValueConstant
+from twisted.internet.defer import fail
 
 from twext.who.idirectory import DirectoryServiceError
 from twext.who.idirectory import NoSuchRecordError, UnknownRecordTypeError
 from twext.who.idirectory import RecordType, FieldName as BaseFieldName
-from twext.who.idirectory import MatchType, QueryFlags
-from twext.who.idirectory import DirectoryQueryMatchExpression
-from twext.who.directory import DirectoryService as BaseDirectoryService
-from twext.who.directory import DirectoryRecord as BaseDirectoryRecord
-from twext.who.util import MergedConstants, describe, uniqueResult, iterFlags
+from twext.who.index import DirectoryService as BaseDirectoryService
+from twext.who.index import DirectoryRecord
+from twext.who.index import FieldName as IndexFieldName
 
 
 
@@ -59,17 +57,6 @@
 
 
 ##
-# Data type extentions
-##
-
-class FieldName(Names):
-    memberUIDs = NamedConstant()
-    memberUIDs.description = "member UIDs"
-    memberUIDs.multiValue = True
-
-
-
-##
 # XML constants
 ##
 
@@ -99,7 +86,7 @@
     password.fieldName = BaseFieldName.password
 
     memberUID = ValueConstant("member-uid")
-    memberUID.fieldName = FieldName.memberUIDs
+    memberUID.fieldName = IndexFieldName.memberUIDs
 
 
 
@@ -136,31 +123,18 @@
     XML directory service.
     """
 
-    fieldName = MergedConstants(BaseDirectoryService.fieldName, FieldName)
-
     element   = Element
     attribute = Attribute
     value     = Value
 
-    indexedFields = (
-        BaseFieldName.recordType,
-        BaseFieldName.uid,
-        BaseFieldName.guid,
-        BaseFieldName.shortNames,
-        BaseFieldName.emailAddresses,
-        FieldName.memberUIDs,
-    )
 
-
     def __init__(self, filePath, refreshInterval=4):
         BaseDirectoryService.__init__(self, realmName=noRealmName)
 
         self.filePath = filePath
         self.refreshInterval = refreshInterval
 
-        self.flush()
 
-
     def __repr__(self):
         realmName = self._realmName
         if realmName is None:
@@ -179,27 +153,25 @@
         self.loadRecords()
         return self._realmName
 
+
     @realmName.setter
     def realmName(self, value):
         if value is not noRealmName:
             raise AssertionError("realmName may not be set directly")
 
+
     @property
     def unknownRecordTypes(self):
         self.loadRecords()
         return self._unknownRecordTypes
 
+
     @property
     def unknownFieldElements(self):
         self.loadRecords()
         return self._unknownFieldElements
 
-    @property
-    def index(self):
-        self.loadRecords()
-        return self._index
 
-
     def loadRecords(self, loadNow=False, stat=True):
         """
         Load records from L{self.filePath}.
@@ -207,8 +179,13 @@
         Does nothing if a successful refresh has happened within the
         last L{self.refreshInterval} seconds.
 
-        @param loadNow: Load now (ignore L{self.refreshInterval})
-        @type loadNow: boolean
+        @param loadNow: If true, load now (ignoring
+            L{self.refreshInterval})
+        @type loadNow: L{type}
+
+        @param stat: If true, check file metadata and don't reload if
+            unchanged.
+        @type loadNow: L{type}
         """
         #
         # Punt if we've read the file recently
@@ -289,11 +266,11 @@
         self._unknownRecordTypes   = unknownRecordTypes
         self._unknownFieldElements = unknownFieldElements
 
-        self._index = index
-
         self._cacheTag = cacheTag
         self._lastRefresh = now
 
+        self.index = index
+
         return etree
 
 
@@ -343,115 +320,15 @@
 
 
     def flush(self):
+        BaseDirectoryService.flush(self)
+
         self._realmName            = None
         self._unknownRecordTypes   = None
         self._unknownFieldElements = None
-        self._index                = None
         self._cacheTag             = None
         self._lastRefresh          = 0
 
 
-    @staticmethod
-    def _queryFlags(flags):
-        predicate = lambda x: x
-        normalize = lambda x: x
-
-        if flags is not None:
-            for flag in iterFlags(flags):
-                if flag == QueryFlags.NOT:
-                    predicate = lambda x: not x
-                elif flag == QueryFlags.caseInsensitive:
-                    normalize = lambda x: x.lower()
-                else:
-                    raise NotImplementedError("Unknown query flag: %s" % (describe(flag),))
-
-        return predicate, normalize
-
-
-    def indexedRecordsFromMatchExpression(self, expression, records=None):
-        """
-        Finds records in the internal indexes matching a single
-        expression.
-        @param expression: an expression
-        @type expression: L{object}
-        """
-        predicate, normalize = self._queryFlags(expression.flags)
-
-        fieldIndex = self.index[expression.fieldName]
-        matchValue = normalize(expression.fieldValue)
-        matchType  = expression.matchType
-
-        if matchType == MatchType.startsWith:
-            indexKeys = (key for key in fieldIndex if predicate(normalize(key).startswith(matchValue)))
-        elif matchType == MatchType.contains:
-            indexKeys = (key for key in fieldIndex if predicate(matchValue in normalize(key)))
-        elif matchType == MatchType.equals:
-            if predicate(True):
-                indexKeys = (matchValue,)
-            else:
-                indexKeys = (key for key in fieldIndex if normalize(key) != matchValue)
-        else:
-            raise NotImplementedError("Unknown match type: %s" % (describe(matchType),))
-
-        matchingRecords = set()
-        for key in indexKeys:
-            matchingRecords |= fieldIndex.get(key, frozenset())
-
-        if records is not None:
-            matchingRecords &= records
-
-        return succeed(matchingRecords)
-
-
-    def unIndexedRecordsFromMatchExpression(self, expression, records=None):
-        """
-        Finds records not in the internal indexes matching a single
-        expression.
-        @param expression: an expression
-        @type expression: L{object}
-        """
-        predicate, normalize = self._queryFlags(expression.flags)
-
-        matchValue = normalize(expression.fieldValue)
-        matchType  = expression.matchType
-
-        if matchType == MatchType.startsWith:
-            match = lambda fieldValue: predicate(fieldValue.startswith(matchValue))
-        elif matchType == MatchType.contains:
-            match = lambda fieldValue: predicate(matchValue in fieldValue)
-        elif matchType == MatchType.equals:
-            match = lambda fieldValue: predicate(fieldValue == matchValue)
-        else:
-            raise NotImplementedError("Unknown match type: %s" % (describe(matchType),))
-
-        result = set()
-
-        if records is None:
-            records = (uniqueResult(values) for values in self.index[self.fieldName.uid].itervalues())
-
-        for record in records:
-            fieldValues = record.fields.get(expression.fieldName, None)
-
-            if fieldValues is None:
-                continue
-
-            for fieldValue in fieldValues:
-                if match(normalize(fieldValue)):
-                    result.add(record)
-
-        return result
-
-
-    def recordsFromExpression(self, expression, records=None):
-        if isinstance(expression, DirectoryQueryMatchExpression):
-            if expression.fieldName in self.indexedFields:
-                return self.indexedRecordsFromMatchExpression(expression, records=records)
-            else:
-                return self.unIndexedRecordsFromMatchExpression(expression, records=records)
-        else:
-            return BaseDirectoryService.recordsFromExpression(self, expression, records=records)
-
-
     def updateRecords(self, records, create=False):
         # Index the records to update by UID
         recordsByUID = dict(((record.uid, record) for record in records))
@@ -554,20 +431,4 @@
 
 
 
-class DirectoryRecord(BaseDirectoryRecord):
-    """
-    XML directory record
-    """
-    @inlineCallbacks
-    def members(self):
-        members = set()
-        for uid in getattr(self, "memberUIDs", ()):
-            members.add((yield self.service.recordWithUID(uid)))
-        returnValue(members)
-
-
-    def groups(self):
-        return self.service.recordsWithFieldValue(FieldName.memberUIDs, self.uid)
-
-
 noRealmName = object()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130215/ddf7d331/attachment-0001.html>


More information about the calendarserver-changes mailing list