[CalendarServer-changes] [4508] CalendarServer/branches/more-deferreds-3

source_changes at macosforge.org source_changes at macosforge.org
Wed Aug 26 10:42:06 PDT 2009


Revision: 4508
          http://trac.macosforge.org/projects/calendarserver/changeset/4508
Author:   sagen at apple.com
Date:     2009-08-26 10:42:05 -0700 (Wed, 26 Aug 2009)
Log Message:
-----------
Checkpoint -- deadProperty access is deferred; all but 3 twisted unit tests pass

Modified Paths:
--------------
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.application.app.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.mail.imap4.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.python.util.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.auth.digest.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.method.report.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.resource.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.static.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.error.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.server.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.static.patch
    CalendarServer/branches/more-deferreds-3/twistedcaldav/directory/calendar.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/mail.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/memcacheprops.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/method/delete_common.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/method/get.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/method/mkcalendar.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/method/put_common.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/method/report_calquery.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/method/report_common.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/resource.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/static.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/test/test_memcacheprops.py
    CalendarServer/branches/more-deferreds-3/twistedcaldav/test/test_resource.py

Added Paths:
-----------
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.auth.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.noneprops.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_delete.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_mkcol.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_move.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_options.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_put.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_report.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_xattrprops.patch
    CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.util.patch

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.application.app.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.application.app.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.application.app.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,6 +1,7 @@
-=== modified file 'twisted/application/app.py'
---- twisted/application/app.py	2008-12-14 22:09:07 +0000
-+++ twisted/application/app.py	2009-07-15 23:30:17 +0000
+Index: twisted/application/app.py
+===================================================================
+--- twisted/application/app.py	(revision 26969)
++++ twisted/application/app.py	(working copy)
 @@ -241,7 +241,11 @@
          @param application: The application on which to check for an
              L{ILogObserver}.
@@ -14,24 +15,25 @@
  
          if observer is None:
              observer = self._getLogObserver()
-@@ -408,8 +412,6 @@
+@@ -408,9 +412,7 @@
          Run the application.
          """
          self.preApplication()
 -        self.application = self.createOrGetApplication()
+ 
 -
- 
          getLogObserverLegacy = getattr(self, 'getLogObserver', None)
          if getLogObserverLegacy is not None:
-@@ -418,7 +420,9 @@
+             warnings.warn("Specifying a log observer with getLogObserver is "
+@@ -418,8 +420,10 @@
                            category=DeprecationWarning)
              self.startLogging(self.getLogObserver())
          else:
 -            self.logger.start(self.application)
 +            self.logger.start(None)
+ 
++        self.application = self.createOrGetApplication()
 +
-+        self.application = self.createOrGetApplication()
- 
          self.postApplication()
          self.logger.stop()
-
+ 

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.mail.imap4.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.mail.imap4.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.mail.imap4.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,6 +1,7 @@
-=== modified file 'twisted/mail/imap4.py'
---- twisted/mail/imap4.py	2009-03-23 11:02:59 +0000
-+++ twisted/mail/imap4.py	2009-07-15 23:30:17 +0000
+Index: twisted/mail/imap4.py
+===================================================================
+--- twisted/mail/imap4.py	(revision 26969)
++++ twisted/mail/imap4.py	(working copy)
 @@ -333,7 +333,7 @@
  
  
@@ -19,4 +20,3 @@
  
  
  class MailboxException(IMAP4Exception): pass
-

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.python.util.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.python.util.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.python.util.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,7 +1,8 @@
-=== modified file 'twisted/python/util.py'
---- twisted/python/util.py	2009-03-30 13:33:33 +0000
-+++ twisted/python/util.py	2009-07-15 23:30:18 +0000
-@@ -569,7 +569,27 @@
+Index: twisted/python/util.py
+===================================================================
+--- twisted/python/util.py	(revision 26969)
++++ twisted/python/util.py	(working copy)
+@@ -569,9 +569,29 @@
      L2.sort()
      return [e[2] for e in L2]
  
@@ -17,8 +18,8 @@
 +if sys.platform == "darwin" and hasCtypes:
 +    import pwd
 +    libc = cdll.LoadLibrary(find_library("libc"))
-+    def initgroups(uid, primaryGid):
-+        """
+     def initgroups(uid, primaryGid):
+         """
 +        Call initgroups with ctypes.
 +        """
 +        c_gid = c_int(primaryGid)
@@ -27,7 +28,8 @@
 +        return libc.initgroups(c_username, c_gid)
 +
 +elif pwd is None or grp is None or setgroups is None or getgroups is None:
-     def initgroups(uid, primaryGid):
-         """
++    def initgroups(uid, primaryGid):
++        """
          Do nothing.
-
+ 
+         Underlying platform support require to manipulate groups is missing.

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.auth.digest.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.auth.digest.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.auth.digest.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,6 +1,7 @@
-=== modified file 'twisted/web2/auth/digest.py'
---- twisted/web2/auth/digest.py	2009-04-20 17:38:06 +0000
-+++ twisted/web2/auth/digest.py	2009-07-15 23:30:18 +0000
+Index: twisted/web2/auth/digest.py
+===================================================================
+--- twisted/web2/auth/digest.py	(revision 26969)
++++ twisted/web2/auth/digest.py	(working copy)
 @@ -138,11 +138,15 @@
      implements(credentials.IUsernameHashedPassword,
                 IUsernameDigestHash)
@@ -18,7 +19,7 @@
  
      def checkPassword(self, password):
          response = self.fields.get('response')
-@@ -155,10 +159,22 @@
+@@ -155,11 +159,23 @@
  
          expected = calcResponse(
              calcHA1(algo, self.username, self.realm, password, nonce, cnonce),
@@ -29,7 +30,7 @@
 -        return expected == response
 +        if expected == response:
 +            return True
-+
+ 
 +        # IE7 sends cnonce and nc values, but auth fails if they are used.
 +        # So try again without them...
 +        # They can be omitted for backwards compatibility [RFC 2069].
@@ -40,9 +41,10 @@
 +            )
 +            if expected == response:
 +                return True
- 
++
      def checkHash(self, digestHash):
          response = self.fields.get('response')
+         uri = self.fields.get('uri')
 @@ -171,7 +187,7 @@
  
          expected = calcResponse(
@@ -78,4 +80,3 @@
 +                                               self.realm,
 +                                               auth,
 +                                               originalMethod))
-

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.auth.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.auth.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.auth.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,27 @@
+Index: twisted/web2/dav/auth.py
+===================================================================
+--- twisted/web2/dav/auth.py	(revision 26969)
++++ twisted/web2/dav/auth.py	(working copy)
+@@ -90,14 +90,18 @@
+         else:
+             raise error.UnauthorizedLogin("Bad credentials for: %s" % (principalURIs[0],))
+ 
+-    def requestAvatarId(self, credentials):
+-        pcreds = IPrincipalCredentials(credentials)
+-        pswd = str(pcreds.authnPrincipal.readDeadProperty(TwistedPasswordProperty))
+-
++    def _cbReadDeadProperty(self, result, credentials, pcreds):
++        pswd = str(result)
+         d = defer.maybeDeferred(credentials.checkPassword, pswd)
+         d.addCallback(self._cbPasswordMatch, (pcreds.authnPrincipal.principalURL(), pcreds.authzPrincipal.principalURL()))
+         return d
+ 
++    def requestAvatarId(self, credentials):
++        pcreds = IPrincipalCredentials(credentials)
++        d = pcreds.authnPrincipal.readDeadProperty(TwistedPasswordProperty)
++        d.addCallback(self._cbReadDeadProperty, credentials, pcreds)
++        return d
++
+ ##
+ # Utilities
+ ##

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.method.put_common.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,41 @@
+Index: twisted/web2/dav/method/put_common.py
+===================================================================
+--- twisted/web2/dav/method/put_common.py	(revision 26969)
++++ twisted/web2/dav/method/put_common.py	(working copy)
+@@ -208,20 +208,31 @@
+         # Update the MD5 value on the resource
+         if source is not None:
+             # Copy MD5 value from source to destination
+-            if source.hasDeadProperty(TwistedGETContentMD5):
+-                md5 = source.readDeadProperty(TwistedGETContentMD5)
+-                destination.writeDeadProperty(md5)
++            hasDeadProperty = waitForDeferred(source.hasDeadProperty(TwistedGETContentMD5))
++            yield hasDeadProperty
++            hasDeadProperty = hasDeadProperty.getResult()
++            if hasDeadProperty:
++                md5 = waitForDeferred(source.readDeadProperty(TwistedGETContentMD5))
++                yield md5
++                md5 = md5.getResult()
++                ignore = waitForDeferred(destination.writeDeadProperty(md5))
++                yield ignore
++                ignore = ignore.getResult( )
+         else:
+             # Finish MD5 calc and write dead property
+             md5.close()
+             md5 = md5.getMD5()
+-            destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
++            ignore = waitForDeferred(destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5)))
++            yield ignore
++            ignore = ignore.getResult( )
+ 
+         # Update the content-type value on the resource if it is not been copied or moved
+         if source is None:
+             content_type = request.headers.getHeader("content-type")
+             if content_type is not None:
+-                destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type)))
++                ignore = destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type)))
++                yield ignore
++                ignore = ignore.getResult( )
+ 
+         response = IResponse(response)
+         

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.method.report.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.method.report.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.method.report.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,6 +1,7 @@
-=== modified file 'twisted/web2/dav/method/report.py'
---- twisted/web2/dav/method/report.py	2009-04-20 17:38:06 +0000
-+++ twisted/web2/dav/method/report.py	2009-07-15 23:30:18 +0000
+Index: twisted/web2/dav/method/report.py
+===================================================================
+--- twisted/web2/dav/method/report.py	(revision 26969)
++++ twisted/web2/dav/method/report.py	(working copy)
 @@ -94,8 +94,9 @@
      namespace = doc.root_element.namespace
      name = doc.root_element.name
@@ -31,4 +32,3 @@
      try:
          method = getattr(self, method_name)
          
-

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.noneprops.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.noneprops.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.noneprops.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,37 @@
+Index: twisted/web2/dav/noneprops.py
+===================================================================
+--- twisted/web2/dav/noneprops.py	(revision 26969)
++++ twisted/web2/dav/noneprops.py	(working copy)
+@@ -33,6 +33,8 @@
+ 
+ from twisted.web2 import responsecode
+ from twisted.web2.http import HTTPError, StatusResponse
++from twisted.python.failure import Failure
++from twisted.internet.defer import succeed
+ 
+ class NonePropertyStore (object):
+     """
+@@ -50,18 +52,18 @@
+         pass
+ 
+     def get(self, qname):
+-        raise HTTPError(StatusResponse(responsecode.NOT_FOUND, "No such property: {%s}%s" % qname))
++        return Failure(HTTPError(StatusResponse(responsecode.NOT_FOUND, "No such property: {%s}%s" % qname)))
+ 
+     def set(self, property):
+-        raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Permission denied for setting property: %s" % (property,)))
++        return Failure(HTTPError(StatusResponse(responsecode.FORBIDDEN, "Permission denied for setting property: %s" % (property,))))
+ 
+     def delete(self, qname):
+         # RFC 2518 Section 12.13.1 says that removal of
+         # non-existing property is not an error.
+-        pass
++        return succeed(None)
+ 
+     def contains(self, qname):
+-        return False
++        return succeed(False)
+ 
+     def list(self):
+-        return ()
++        return succeed( () )

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.resource.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.resource.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,6 +1,7 @@
-=== modified file 'twisted/web2/dav/resource.py'
---- twisted/web2/dav/resource.py	2009-05-07 20:35:07 +0000
-+++ twisted/web2/dav/resource.py	2009-07-15 23:30:18 +0000
+Index: twisted/web2/dav/resource.py
+===================================================================
+--- twisted/web2/dav/resource.py	(revision 26969)
++++ twisted/web2/dav/resource.py	(working copy)
 @@ -49,12 +49,14 @@
  if not hasattr(__builtin__, "frozenset"):
      import sets.ImmutableSet as frozenset
@@ -18,15 +19,197 @@
  from twisted.internet import reactor
  from twisted.web2 import responsecode
  from twisted.web2.http import HTTPError, RedirectResponse, StatusResponse
-@@ -578,7 +580,6 @@
+@@ -186,9 +188,15 @@
+                 d = self.hasQuota(request)
+                 d.addCallback(lambda result: result)
+                 return d
+-        
+-        return succeed(qname in self.liveProperties or self.deadProperties().contains(qname))
  
++        if qname in self.liveProperties:
++            return succeed(True)
++
++        d = self.deadProperties().contains(qname)
++        d.addCallback(lambda result: result)
++        return d
++        # return succeed(qname in self.liveProperties or self.deadProperties().contains(qname))
++
+     def readProperty(self, property, request):
+         """
+         See L{IDAVResource.readProperty}.
+@@ -206,24 +214,43 @@
+             if namespace == dav_namespace:
+                 if name == "resourcetype":
+                     # Allow live property to be overriden by dead property
+-                    if self.deadProperties().contains(qname):
+-                        return self.deadProperties().get(qname)
+-                    if self.isCollection():
+-                        return davxml.ResourceType.collection
+-                    return davxml.ResourceType.empty
++                    def callback(result):
++                        if result:
++                            return self.deadProperties().get(qname)
++                        else:
++                            if self.isCollection():
++                                return davxml.ResourceType.collection
++                            return davxml.ResourceType.empty
+ 
++                    d = self.deadProperties().contains(qname)
++                    d.addCallback(callback)
++                    return d
++
+                 if name == "getetag":
+-                    etag = self.etag()
+-                    if etag is None:
+-                        return None
+-                    return davxml.GETETag(etag.generate())
++                    def etagCallback(result):
++                        if result is None:
++                            return None
++                        return davxml.GETETag(result.generate())
+ 
++                    d = self.etag()
++                    d.addCallback(etagCallback)
++                    return d
++
++                    # etag = self.etag()
++                    # if etag is None:
++                    #     return None
++                    # return davxml.GETETag(etag.generate())
++
+                 if name == "getcontenttype":
+-                    mimeType = self.contentType()
+-                    if mimeType is None:
+-                        return None
+-                    return davxml.GETContentType(generateContentType(mimeType))
++                    def contentTypeCallback(result):
++                        if result is None:
++                            return None
++                        return davxml.GETContentType(generateContentType(result))
++                    d = self.contentType()
++                    d.addCallback(contentTypeCallback)
++                    return d
+ 
++
+                 if name == "getcontentlength":
+                     length = self.contentLength()
+                     if length is None:
+@@ -247,11 +274,15 @@
+                     return davxml.CreationDate.fromDate(creationDate)
+ 
+                 if name == "displayname":
+-                    displayName = self.displayName()
+-                    if displayName is None:
+-                        return None
+-                    return davxml.DisplayName(displayName)
++                    def displaynameCallback(result):
++                        if result is None:
++                            return None
++                        return davxml.DisplayName(result)
+ 
++                    d = self.displayName()
++                    d.addCallback(displaynameCallback)
++                    return d
++
+                 if name == "supportedlock":
+                     return davxml.SupportedLock(
+                         davxml.LockEntry(davxml.LockScope.exclusive, davxml.LockType.write),
+@@ -426,7 +457,10 @@
+             if not has:
+                 qnames.remove(dqname)
+ 
+-        for qname in self.deadProperties().list():
++        qnamesList = waitForDeferred(self.deadProperties().list())
++        yield qnamesList
++        qnamesList = qnamesList.getResult()
++        for qname in qnamesList:
+             if (qname not in qnames) and (qname[0] != twisted_private_namespace):
+                 qnames.add(qname)
+ 
+@@ -495,37 +529,54 @@
+         in the dead property store may or may not be ignored when reading the
+         property with L{readProperty}.
+         """
+-        self.deadProperties().set(property)
++        return self.deadProperties().set(property)
+ 
+     def removeDeadProperty(self, property):
+         """
+         Same as L{removeProperty}, but bypasses the live property store and acts
+         directly on the dead property store.
+         """
+-        if self.hasDeadProperty(property):
+-            if type(property) is tuple:
+-                qname = property
++        def callback(result):
++            if result:
++                if type(property) is tuple:
++                    qname = property
++                else:
++                    qname = property.qname()
++                # MOR: Double check I can return a deferred here
++                return self.deadProperties().delete(qname)
+             else:
+-                qname = property.qname()
++                return succeed(None) # Is this necessary?
+ 
+-            self.deadProperties().delete(qname)
++        d = self.hasDeadProperty(property)
++        d.addCallback(callback)
++        return d
+ 
++
+     #
+     # Overrides some methods in MetaDataMixin in order to allow DAV properties
+     # to override the values of some HTTP metadata.
+     #
+     def contentType(self):
+-        if self.hasDeadProperty((davxml.dav_namespace, "getcontenttype")):
+-            return self.readDeadProperty((davxml.dav_namespace, "getcontenttype")).mimeType()
+-        else:
+-            return super(DAVPropertyMixIn, self).contentType()
++        def callback(result):
++            if result:
++                return self.readDeadProperty((davxml.dav_namespace, "getcontenttype")).mimeType()
++            else:
++                return super(DAVPropertyMixIn, self).contentType()
++        d = self.hasDeadProperty((davxml.dav_namespace, "getcontenttype"))
++        d.addCallback(callback)
++        return d
+ 
+     def displayName(self):
+-        if self.hasDeadProperty((davxml.dav_namespace, "displayname")):
+-            return str(self.readDeadProperty((davxml.dav_namespace, "displayname")))
+-        else:
+-            return super(DAVPropertyMixIn, self).displayName()
++        def callback(result):
++            if result:
++                return str(self.readDeadProperty((davxml.dav_namespace, "displayname")))
++            else:
++                return super(DAVPropertyMixIn, self).displayName()
+ 
++        d = self.hasDeadProperty((davxml.dav_namespace, "displayname"))
++        d.addCallback(callback)
++        return d
++
+ class DAVResource (DAVPropertyMixIn, StaticRenderMixin):
+     """
+     WebDAV resource.
+@@ -578,11 +629,10 @@
+ 
          completionDeferred = Deferred()
          basepath = request.urlForResource(self)
 -        children = list(self.listChildren())
  
-         def checkPrivilegesError(failure):
+-        def checkPrivilegesError(failure):
++        def checkPrivilegesError(failure, children):
              failure.trap(AccessDeniedError)
-@@ -595,7 +596,7 @@
+-            reactor.callLater(0, getChild)
++            reactor.callLater(0, getChild, children)
+ 
+         def checkPrivileges(child):
+             if child is None:
+@@ -595,7 +645,7 @@
              d.addCallback(lambda _: child)
              return d
  
@@ -35,7 +218,7 @@
              if child is None:
                  callback(None, childpath + "/")
              else:
-@@ -603,14 +604,15 @@
+@@ -603,14 +653,15 @@
                      callback(child, childpath + "/")
                      if depth == "infinity":
                          d = child.findChildren(depth, request, callback, privileges)
@@ -54,12 +237,12 @@
              try:
                  childname = children.pop()
              except IndexError:
-@@ -619,10 +621,10 @@
+@@ -619,10 +670,10 @@
                  childpath = joinURL(basepath, childname)
                  d = request.locateChildResource(self, childname)
                  d.addCallback(checkPrivileges)
 -                d.addCallbacks(gotChild, checkPrivilegesError, (childpath,))
-+                d.addCallbacks(gotChild, checkPrivilegesError, (childpath, children))
++                d.addCallbacks(gotChild, checkPrivilegesError, callbackArgs=(childpath, children), errbackArgs=(children,))
                  d.addErrback(completionDeferred.errback)
  
 -        getChild()
@@ -67,7 +250,7 @@
  
          return completionDeferred
  
-@@ -642,39 +644,41 @@
+@@ -642,41 +693,43 @@
      # Authentication
      ##
  
@@ -97,15 +280,10 @@
 -                # "Authorization will not help" according to RFC2616
 -                #
 -                raise HTTPError(response)
--
+ 
 -            d = self.checkPrivileges(request, privileges, recurse)
 -            d.addErrback(onErrors)
 -            return d
--
--        d = maybeDeferred(self.authenticate, request)
--        d.addCallback(onAuth)
--        return d
-+
 +        try:
 +            yield self.authenticate(request)
 +        except (UnauthorizedLogin, LoginFailed), e:
@@ -115,7 +293,10 @@
 +                request.remoteAddr
 +            ))
 +            raise HTTPError(response)
-+
+ 
+-        d = maybeDeferred(self.authenticate, request)
+-        d.addCallback(onAuth)
+-        return d
 +        try:
 +            yield self.checkPrivileges(request, privileges, recurse)
 +        except AccessDeniedError, e:
@@ -134,11 +315,220 @@
 +            # "Authorization will not help" according to RFC2616
 +            #
 +            raise HTTPError(response)
+ 
+ 
 +
+     def authenticate(self, request):
+         if not (
+             hasattr(request, 'portal') and 
+@@ -781,7 +834,7 @@
+         This implementation stores the ACL in the private property
+         C{(L{twisted_private_namespace}, "acl")}.
+         """
+-        self.writeDeadProperty(acl)
++        return self.writeDeadProperty(acl)
  
+     def mergeAccessControlList(self, new_acl, request):
+         """
+@@ -1089,7 +1142,9 @@
+             return url
  
-     def authenticate(self, request):
-@@ -1880,7 +1884,7 @@
+         try:
+-            acl = self.readDeadProperty(davxml.ACL)
++            acl = waitForDeferred(self.readDeadProperty(davxml.ACL))
++            yield acl
++            acl = acl.getResult()
+         except HTTPError, e:
+             assert e.response.code == responsecode.NOT_FOUND, (
+                 "Expected %s response from readDeadProperty() exception, not %s"
+@@ -1635,7 +1690,9 @@
+ 
+         # Check this resource first
+         if self.isCollection():
+-            qroot = self.quotaRoot(request)
++            qroot = waitForDeferred(self.quotaRoot(request))
++            yield qroot
++            qroot = qroot.getResult()
+             if qroot is not None:
+                 used = waitForDeferred(self.currentQuotaUse(request))
+                 yield used
+@@ -1673,7 +1730,10 @@
+         """
+         
+         # Check this one first
+-        if self.hasQuotaRoot(request):
++        hasQuotaRoot = waitForDeferred(self.hasQuotaRoot(request))
++        yield hasQuotaRoot
++        hasQuotaRoot = hasQuotaRoot.getResult()
++        if hasQuotaRoot:
+             yield True
+             return
+         
+@@ -1705,10 +1765,19 @@
+         @return: a C{int} containing the maximum allowed bytes if this collection
+             is quota-controlled, or C{None} if not quota controlled.
+         """
+-        if self.hasDeadProperty(TwistedQuotaRootProperty):
+-            return int(str(self.readDeadProperty(TwistedQuotaRootProperty)))
++        hasDeadProperty = waitForDeferred(self.hasDeadProperty(TwistedQuotaRootProperty))
++        yield hasDeadProperty
++        hasDeadProperty = hasDeadProperty.getResult()
++
++        if hasDeadProperty:
++            propValue = waitForDeferred(self.readDeadProperty(TwistedQuotaRootProperty))
++            yield propValue
++            propValue = propValue.getResult()
++            yield int(str(propValue))
+         else:
+-            return None
++            yield None
++
++    quotaRoot = deferredGenerator(quotaRoot)
+     
+     def quotaRootParent(self, request):
+         """
+@@ -1724,7 +1793,10 @@
+             parent = waitForDeferred(request.locateResource(url))
+             yield parent
+             parent = parent.getResult()
+-            if parent.hasQuotaRoot(request):
++            hasQuotaRoot = waitForDeferred(parent.hasQuotaRoot(request))
++            yield hasQuotaRoot
++            hasQuotaRoot = hasQuotaRoot.getResult()
++            if hasQuotaRoot:
+                 yield parent
+                 return
+ 
+@@ -1741,11 +1813,19 @@
+         assert maxsize is None or isinstance(maxsize, int), "maxsize must be an int or None"
+         
+         if maxsize is not None:
+-            self.writeDeadProperty(TwistedQuotaRootProperty(str(maxsize)))
++            d = waitForDeferred(self.writeDeadProperty(TwistedQuotaRootProperty(str(maxsize))))
++            yield d
++            d.getResult()
+         else:
+             # Remove both the root and the cached used value
+-            self.removeDeadProperty(TwistedQuotaRootProperty)
+-            self.removeDeadProperty(TwistedQuotaUsedProperty)
++            d = waitForDeferred(self.removeDeadProperty(TwistedQuotaRootProperty))
++            yield d
++            d.getResult()
++            d = waitForDeferred(self.removeDeadProperty(TwistedQuotaUsedProperty))
++            yield d
++            d.getResult()
++
++    setQuotaRoot = deferredGenerator(setQuotaRoot)
+     
+     def quotaSize(self, request):
+         """
+@@ -1795,7 +1875,10 @@
+         
+         # Check this resource first
+         if self.isCollection():
+-            if self.hasQuotaRoot(request):
++            hasQuotaRoot = waitForDeferred(self.hasQuotaRoot(request))
++            yield hasQuotaRoot
++            hasQuotaRoot = hasQuotaRoot.getResult()
++            if hasQuotaRoot:
+                 d = waitForDeferred(self.updateQuotaUse(request, adjust))
+                 yield d
+                 d.getResult()
+@@ -1825,20 +1908,34 @@
+             is quota-controlled, or C{None} if not quota controlled.
+         """
+         assert self.isCollection(), "Only collections can have a quota root"
+-        assert self.hasQuotaRoot(request), "Quota use only on quota root collection"
++        hasQuotaRoot = waitForDeferred(self.hasQuotaRoot(request))
++        yield hasQuotaRoot
++        hasQuotaRoot = hasQuotaRoot.getResult()
++        assert hasQuotaRoot, "Quota use only on quota root collection"
+         
+         # Try to get the cached value property
+-        if self.hasDeadProperty(TwistedQuotaUsedProperty):
+-            return succeed(int(str(self.readDeadProperty(TwistedQuotaUsedProperty))))
++        hasDeadProperty = waitForDeferred(self.hasDeadProperty(TwistedQuotaUsedProperty))
++        yield hasDeadProperty
++        hasDeadProperty = hasDeadProperty.getResult()
++        if hasDeadProperty:
++            propValue = waitForDeferred(self.readDeadProperty(TwistedQuotaUsedProperty))
++            yield propValue
++            propValue = propValue.getResult()
++            yield int(str(propValue))
++            return
+         else:
+             # Do brute force size determination and cache the result in the private property
+-            def _defer(result):
+-                self.writeDeadProperty(TwistedQuotaUsedProperty(str(result)))
+-                return result
+-            d = self.quotaSize(request)
+-            d.addCallback(_defer)
+-            return d
++            quotaSize = waitForDeferred(self.quotaSize(request))
++            yield quotaSize
++            quotaSize = quotaSize.getResult()
++            d = waitForDeferred(self.writeDeadProperty(TwistedQuotaUsedProperty(str(quotaSize))))
++            yield d
++            d = d.getResult()
++            yield quotaSize
++            return
+ 
++    currentQuotaUse = deferredGenerator(currentQuotaUse)
++
+     def updateQuotaUse(self, request, adjust):
+         """
+         Update the quota used value on this resource.
+@@ -1848,25 +1945,32 @@
+         @return: an L{Deferred} with a C{int} result containing the current used byte if this collection
+             is quota-controlled, or C{None} if not quota controlled.
+         """
++
+         assert self.isCollection(), "Only collections can have a quota root"
+-        
+-        # Get current value
+-        def _defer(size):
+-            size += adjust
+-            
+-            # Sanity check the resulting size
+-            if size >= 0:
+-                self.writeDeadProperty(TwistedQuotaUsedProperty(str(size)))
+-            else:
+-                # Remove the dead property and re-read to do brute force quota calc
+-                log.msg("Attempt to set quota used to a negative value: %s (adjustment: %s)" % (size, adjust,))
+-                self.removeDeadProperty(TwistedQuotaUsedProperty)
+-                return self.currentQuotaUse(request)
++        size = waitForDeferred(self.currentQuotaUse(request))
++        yield size
++        size = size.getResult()
++        size += adjust
+ 
+-        d = self.currentQuotaUse(request)
+-        d.addCallback(_defer)
+-        return d
+-        
++        # Sanity check the resulting size
++        if size >= 0:
++            d = waitForDeferred(self.writeDeadProperty(TwistedQuotaUsedProperty(str(size))))
++            yield d
++            d = d.getResult()
++        else:
++            # Remove the dead property and re-read to do brute force quota calc
++            log.msg("Attempt to set quota used to a negative value: %s (adjustment: %s)" % (size, adjust,))
++            d = waitForDeferred(self.removeDeadProperty(TwistedQuotaUsedProperty))
++            yield d
++            d.getResult()
++            size = waitForDeferred(self.currentQuotaUse(request))
++            yield size
++            size = size.getResult()
++        yield size
++        return
++
++    updateQuotaUse = deferredGenerator(updateQuotaUse)
++
+     ##
+     # HTTP
+     ##
+@@ -1880,7 +1984,7 @@
          # If this is a collection and the URI doesn't end in "/", redirect.
          #
          if self.isCollection() and request.path[-1:] != "/":
@@ -147,4 +537,3 @@
  
          def setHeaders(response):
              response = IResponse(response)
-

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.static.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.static.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.static.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,6 +1,7 @@
-=== modified file 'twisted/web2/dav/static.py'
---- twisted/web2/dav/static.py	2009-04-20 17:38:06 +0000
-+++ twisted/web2/dav/static.py	2009-07-15 23:30:18 +0000
+Index: twisted/web2/dav/static.py
+===================================================================
+--- twisted/web2/dav/static.py	(revision 26969)
++++ twisted/web2/dav/static.py	(working copy)
 @@ -53,6 +53,14 @@
  
      Extends twisted.web2.static.File to handle WebDAV methods.
@@ -16,7 +17,38 @@
      def __init__(
          self, path,
          defaultType="text/plain", indexNames=None,
-@@ -192,7 +200,7 @@
+@@ -82,12 +90,26 @@
+     ##
+ 
+     def etag(self):
+-        if not self.fp.exists(): return None
+-        if self.hasDeadProperty(TwistedGETContentMD5):
+-            return http_headers.ETag(str(self.readDeadProperty(TwistedGETContentMD5)))
++        if not self.fp.exists():
++            yield None
++            return
++
++        hasProp = waitForDeferred(self.hasDeadProperty(TwistedGETContentMD5))
++        yield hasProp
++        hasProp = hasProp.getResult()
++        if hasProp:
++            propValue = waitForDeferred(self.readDeadProperty(TwistedGETContentMD5))
++            yield propValue
++            propValue = propValue.getResult()
++            yield http_headers.ETag(str(propValue))
+         else:
+-            return super(DAVFile, self).etag()
++            d = waitForDeferred(super(DAVFile, self).etag())
++            yield d
++            d = d.getResult()
++            yield d
+ 
++    etag = deferredGenerator(etag)
++
+     def davComplianceClasses(self):
+         return ("1", "access-control") # Add "2" when we have locking
+ 
+@@ -192,7 +214,7 @@
          return (self.createSimilarFile(self.fp.child(path).path), segments[1:])
  
      def createSimilarFile(self, path):
@@ -25,4 +57,3 @@
              path, defaultType=self.defaultType, indexNames=self.indexNames[:],
              principalCollections=self.principalCollections())
  
-

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_acl.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,479 @@
+Index: twisted/web2/dav/test/test_acl.py
+===================================================================
+--- twisted/web2/dav/test/test_acl.py	(revision 26969)
++++ twisted/web2/dav/test/test_acl.py	(working copy)
+@@ -26,6 +26,8 @@
+ 
+ from twisted.cred.portal import Portal
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
++
+ from twisted.web2 import responsecode
+ from twisted.web2.auth import basic
+ from twisted.web2.stream import MemoryStream
+@@ -68,7 +70,9 @@
+         os.mkdir(docroot)
+ 
+         userResource = TestDAVPrincipalResource("/principals/users/user01")
+-        userResource.writeDeadProperty(TwistedPasswordProperty("user01"))
++        d = waitForDeferred(userResource.writeDeadProperty(TwistedPasswordProperty("user01")))
++        yield d
++        d = d.getResult()
+ 
+         principalCollection = TestPrincipalsCollection(
+             "/principals/",
+@@ -118,8 +122,9 @@
+                 os.mkdir(dirname)
+             resource = self.resource_class(dirname)
+             resource.setAccessControlList(acl)
+-        return docroot
++        yield docroot
+ 
++    createDocumentRoot = deferredGenerator(createDocumentRoot)
+ 
+     def restore(self):
+         # Get rid of whatever messed up state the test has now so that we'll
+@@ -134,24 +139,32 @@
+         Verify source access controls during COPY and MOVE.
+         """
+         def work():
+-            dst_path = os.path.join(self.docroot, "copy_dst")
++            docroot = waitForDeferred(self.docroot)
++            yield docroot
++            docroot = docroot.getResult()
++            dst_path = os.path.join(docroot, "copy_dst")
+             dst_uri = "/" + os.path.basename(dst_path)
+ 
++            results = []
+             for src, status in (
+                 ("nobind", responsecode.FORBIDDEN),
+                 ("bind",   responsecode.FORBIDDEN),
+                 ("unbind", responsecode.CREATED),
+             ):
+-                src_path = os.path.join(self.docroot, "src_" + src)
++                src_path = os.path.join(docroot, "src_" + src)
+                 src_uri = "/" + os.path.basename(src_path)
+                 if not os.path.isdir(src_path):
+                     os.mkdir(src_path)
+                 src_resource = self.resource_class(src_path)
+-                src_resource.setAccessControlList({
++
++                d = waitForDeferred(src_resource.setAccessControlList({
+                     "nobind": self.grant(),
+                     "bind"  : self.grant(davxml.Bind()),
+                     "unbind": self.grant(davxml.Bind(), davxml.Unbind())
+-                }[src])
++                }[src]))
++                yield d
++                d.getResult()
++
+                 for name, acl in (
+                     ("none"       , self.grant()),
+                     ("read"       , self.grant(davxml.Read())),
+@@ -162,8 +175,11 @@
+                     filename = os.path.join(src_path, name)
+                     if not os.path.isfile(filename):
+                         file(filename, "w").close()
+-                    self.resource_class(filename).setAccessControlList(acl)
+ 
++                    d = waitForDeferred(self.resource_class(filename).setAccessControlList(acl))
++                    yield d
++                    d.getResult()
++
+                 for method in ("COPY", "MOVE"):
+                     for name, code in (
+                         ("none"       , {"COPY": responsecode.FORBIDDEN, "MOVE": status}[method]),
+@@ -175,7 +191,11 @@
+                         path = os.path.join(src_path, name)
+                         uri = src_uri + "/" + name
+     
+-                        request = SimpleRequest(self.site, method, uri)
++                        site = waitForDeferred(self.site)
++                        yield site
++                        site = site.getResult()
++
++                        request = SimpleRequest(site, method, uri)
+                         request.headers.setHeader("destination", dst_uri)
+                         _add_auth_header(request)
+     
+@@ -184,30 +204,56 @@
+                                 os.remove(dst_path)
+     
+                             if response.code != code:
+-                                return self.oops(request, response, code, method, name)
++                                d = waitForDeferred(self.oops(request, response, code, method, name))
++                                yield d
++                                d = d.getResult()
++                                yield d
++                                return
+     
+-                        yield (request, test)
++                        results.append( (request, test) )
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_MOVE_source = deferredGenerator(test_COPY_MOVE_source)
++
++
+     def test_COPY_MOVE_dest(self):
+         """
+         Verify destination access controls during COPY and MOVE.
+         """
+         def work():
+-            src_path = os.path.join(self.docroot, "read")
++            docroot = waitForDeferred(self.docroot)
++            yield docroot
++            docroot = docroot.getResult()
++            src_path = os.path.join(docroot, "read")
+             uri = "/" + os.path.basename(src_path)
+ 
++            results = []
+             for method in ("COPY", "MOVE"):
+                 for name, code in (
+                     ("nobind" , responsecode.FORBIDDEN),
+                     ("bind"   , responsecode.CREATED),
+                     ("unbind" , responsecode.CREATED),
+                 ):
+-                    dst_parent_path = os.path.join(self.docroot, name)
++                    dst_parent_path = os.path.join(docroot, name)
+                     dst_path = os.path.join(dst_parent_path, "dst")
+ 
+-                    request = SimpleRequest(self.site, method, uri)
++                    site = waitForDeferred(self.site)
++                    yield site
++                    site = site.getResult()
++
++                    request = SimpleRequest(site, method, uri)
+                     request.headers.setHeader("destination", "/" + name + "/dst")
+                     _add_auth_header(request)
+ 
+@@ -216,39 +262,80 @@
+                             os.remove(dst_path)
+ 
+                         if response.code != code:
+-                            return self.oops(request, response, code, method, name)
++                            d = waitForDeferred(self.oops(request, response, code, method, name))
++                            yield d
++                            d = d.getResult()
++                            yield d
++                            return
+ 
+-                    yield (request, test)
++                    results.append((request, test))
+                     self.restore()
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_MOVE_dest = deferredGenerator(test_COPY_MOVE_dest)
++
+     def test_DELETE(self):
+         """
+         Verify access controls during DELETE.
+         """
+         def work():
++            docroot = waitForDeferred(self.docroot)
++            yield docroot
++            docroot = docroot.getResult()
++            results = []
+             for name, code in (
+                 ("nobind" , responsecode.FORBIDDEN),
+                 ("bind"   , responsecode.FORBIDDEN),
+                 ("unbind" , responsecode.NO_CONTENT),
+             ):
+-                collection_path = os.path.join(self.docroot, name)
++                collection_path = os.path.join(docroot, name)
+                 path = os.path.join(collection_path, "dst")
+ 
+                 file(path, "w").close()
+ 
+-                request = SimpleRequest(self.site, "DELETE", "/" + name + "/dst")
++                site = waitForDeferred(self.site)
++                yield site
++                site = site.getResult()
++
++                request = SimpleRequest(site, "DELETE", "/" + name + "/dst")
+                 _add_auth_header(request)
+ 
+                 def test(response, code=code, path=path):
+                     if response.code != code:
+-                        return self.oops(request, response, code, "DELETE", name)
++                        d = waitForDeferred(self.oops(request, response, code, "DELETE", name))
++                        yield d
++                        d = d.getResult()
++                        yield d
++                        return
+ 
+-                yield (request, test)
++                results.append((request, test))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++
+     def test_UNLOCK(self):
+         """
+         Verify access controls during UNLOCK of unowned lock.
+@@ -261,14 +348,19 @@
+         """
+         Verify access controls during MKCOL.
+         """
+-        for method in ("MKCOL", "PUT"):
+-            def work():
++        def work():
++            docroot = waitForDeferred(self.docroot)
++            yield docroot
++            docroot = docroot.getResult()
++            results = []
++
++            for method in ("MKCOL", "PUT"):
+                 for name, code in (
+                     ("nobind" , responsecode.FORBIDDEN),
+                     ("bind"   , responsecode.CREATED),
+                     ("unbind" , responsecode.CREATED),
+                 ):
+-                    collection_path = os.path.join(self.docroot, name)
++                    collection_path = os.path.join(docroot, name)
+                     path = os.path.join(collection_path, "dst")
+ 
+                     if os.path.isfile(path):
+@@ -276,22 +368,45 @@
+                     elif os.path.isdir(path):
+                         os.rmdir(path)
+ 
+-                    request = SimpleRequest(self.site, method, "/" + name + "/dst")
++                    site = waitForDeferred(self.site)
++                    yield site
++                    site = site.getResult()
++                    request = SimpleRequest(site, method, "/" + name + "/dst")
+                     _add_auth_header(request)
+ 
+                     def test(response, code=code, path=path):
+                         if response.code != code:
+-                            return self.oops(request, response, code, method, name)
++                            d = waitForDeferred(self.oops(request, response, code, "DELETE", name))
++                            yield d
++                            d = d.getResult()
++                            yield d
++                            return
+ 
+-                    yield (request, test)
++                    results.append((request, test))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++
+     def test_PUT_exists(self):
+         """
+         Verify access controls during PUT of existing file.
+         """
+         def work():
++            docroot = waitForDeferred(self.docroot)
++            yield docroot
++            docroot = docroot.getResult()
++            results = []
+             for name, code in (
+                 ("none"       , responsecode.FORBIDDEN),
+                 ("read"       , responsecode.FORBIDDEN),
+@@ -299,19 +414,38 @@
+                 ("unlock"     , responsecode.FORBIDDEN),
+                 ("all"        , responsecode.NO_CONTENT),
+             ):
+-                path = os.path.join(self.docroot, name)
++                path = os.path.join(docroot, name)
+ 
+-                request = SimpleRequest(self.site, "PUT", "/" + name)
++                site = waitForDeferred(self.site)
++                yield site
++                site = site.getResult()
++                request = SimpleRequest(site, "PUT", "/" + name)
+                 _add_auth_header(request)
+ 
+                 def test(response, code=code, path=path):
+                     if response.code != code:
+-                        return self.oops(request, response, code, "PUT", name)
++                        d = waitForDeferred(self.oops(request, response, code, "PUT", name))
++                        yield d
++                        d = d.getResult()
++                        yield d
++                        return
+ 
+-                yield (request, test)
++                results.append((request, test))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++
+     def test_PROPFIND(self):
+         """
+         Verify access controls during PROPFIND.
+@@ -325,6 +459,10 @@
+         Verify access controls during PROPPATCH.
+         """
+         def work():
++            docroot = waitForDeferred(self.docroot)
++            yield docroot
++            docroot = docroot.getResult()
++            results = []
+             for name, code in (
+                 ("none"       , responsecode.FORBIDDEN),
+                 ("read"       , responsecode.FORBIDDEN),
+@@ -332,9 +470,12 @@
+                 ("unlock"     , responsecode.FORBIDDEN),
+                 ("all"        , responsecode.MULTI_STATUS),
+             ):
+-                path = os.path.join(self.docroot, name)
++                path = os.path.join(docroot, name)
+ 
+-                request = SimpleRequest(self.site, "PROPPATCH", "/" + name)
++                site = waitForDeferred(self.site)
++                yield site
++                site = site.getResult()
++                request = SimpleRequest(site, "PROPPATCH", "/" + name)
+                 request.stream = MemoryStream(
+                     davxml.WebDAVDocument(davxml.PropertyUpdate()).toxml()
+                 )
+@@ -342,17 +483,36 @@
+ 
+                 def test(response, code=code, path=path):
+                     if response.code != code:
+-                        return self.oops(request, response, code, "PROPPATCH", name)
++                        d = waitForDeferred(self.oops(request, response, code, "PROPPATCH", name))
++                        yield d
++                        d = d.getResult()
++                        yield d
++                        return
+ 
+-                yield (request, test)
++                results.append((request, test))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
+     def test_GET_REPORT(self):
+         """
+         Verify access controls during GET and REPORT.
+         """
+         def work():
++            docroot = waitForDeferred(self.docroot)
++            yield docroot
++            docroot = docroot.getResult()
++            results = []
+             for method in ("GET", "REPORT"):
+                 if method == "GET":
+                     ok = responsecode.OK
+@@ -368,9 +528,12 @@
+                     ("unlock"     , responsecode.FORBIDDEN),
+                     ("all"        , ok),
+                 ):
+-                    path = os.path.join(self.docroot, name)
++                    path = os.path.join(docroot, name)
+ 
+-                    request = SimpleRequest(self.site, method, "/" + name)
++                    site = waitForDeferred(self.site)
++                    yield site
++                    site = site.getResult()
++                    request = SimpleRequest(site, method, "/" + name)
+                     if method == "REPORT":
+                         request.stream = MemoryStream(davxml.PrincipalPropertySearch().toxml())
+ 
+@@ -378,12 +541,27 @@
+ 
+                     def test(response, code=code, path=path):
+                         if response.code != code:
+-                            return self.oops(request, response, code, method, name)
++                            d = waitForDeferred(self.oops(request, response, code, method, name))
++                            yield d
++                            d = d.getResult()
++                            yield d
+ 
+-                    yield (request, test)
++                    results.append((request, test))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++
+     def oops(self, request, response, code, method, name):
+         def gotResponseData(doc):
+             if doc is None:

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_copy.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,136 @@
+Index: twisted/web2/dav/test/test_copy.py
+===================================================================
+--- twisted/web2/dav/test/test_copy.py	(revision 26969)
++++ twisted/web2/dav/test/test_copy.py	(working copy)
+@@ -26,6 +26,7 @@
+ import os
+ import urllib
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ import twisted.web2.dav.test.util
+ from twisted.web2 import responsecode
+ from twisted.web2.test.test_server import SimpleRequest
+@@ -78,8 +79,17 @@
+                 self.fail("Source %s is neither a file nor a directory"
+                           % (path,))
+ 
+-        return serialize(self.send, work(self, test))
++        d = waitForDeferred(work(self, test))
++        yield d
++        d = d.getResult( )
+ 
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_create = deferredGenerator(test_COPY_create)
++
+     def test_COPY_exists(self):
+         """
+         COPY to existing resource.
+@@ -92,8 +102,18 @@
+                 # FIXME: Check XML error code (2518bis)
+                 pass
+ 
+-        return serialize(self.send, work(self, test, overwrite=False))
++        d = waitForDeferred(work(self, test, overwrite=False))
++        yield d
++        d = d.getResult( )
+ 
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_exists = deferredGenerator(test_COPY_exists)
++
++
+     def test_COPY_overwrite(self):
+         """
+         COPY to existing resource with overwrite header.
+@@ -108,8 +128,17 @@
+ 
+             self.failUnless(os.path.exists(dst_path), "COPY didn't produce file: %s" % (dst_path,))
+ 
+-        return serialize(self.send, work(self, test, overwrite=True))
++        d = waitForDeferred(work(self, test, overwrite=True))
++        yield d
++        d = d.getResult( )
+ 
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_overwrite = deferredGenerator(test_COPY_overwrite)
++
+     def test_COPY_no_parent(self):
+         """
+         COPY to resource with no parent.
+@@ -122,18 +151,35 @@
+                 # FIXME: Check XML error code (2518bis)
+                 pass
+ 
+-        return serialize(self.send, work(self, test, dst=os.path.join(self.docroot, "elvislives!")))
++        docroot = waitForDeferred(self.docroot)
++        yield docroot
++        docroot = docroot.getResult()
+ 
++        d = waitForDeferred(work(self, test, dst=os.path.join(docroot, "elvislives!")))
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_COPY_no_parent = deferredGenerator(test_COPY_no_parent)
++
+ def work(self, test, overwrite=None, dst=None, depths=("0", "infinity", None)):
++    docroot = waitForDeferred(self.docroot)
++    yield docroot
++    docroot = docroot.getResult()
++    results = []
+     if dst is None:
+-        dst = os.path.join(self.docroot, "dst")
++        dst = os.path.join(docroot, "dst")
+         os.mkdir(dst)
+ 
+-    for basename in os.listdir(self.docroot):
++    for basename in os.listdir(docroot):
+         if basename == "dst": continue
+ 
+         uri = urllib.quote("/" + basename)
+-        path = os.path.join(self.docroot, basename)
++        path = os.path.join(docroot, basename)
+         isfile = os.path.isfile(path)
+         sum = sumFile(path)
+         dst_path = os.path.join(dst, basename)
+@@ -151,15 +197,23 @@
+             def do_test(response, path=path, isfile=isfile, sum=sum, uri=uri, depth=depth, dst_path=dst_path):
+                 test(response, path, isfile, sum, uri, depth, dst_path)
+ 
+-            request = SimpleRequest(self.site, self.__class__.__name__, uri)
++            site = waitForDeferred(self.site)
++            yield site
++            site = site.getResult()
++            request = SimpleRequest(site, self.__class__.__name__, uri)
+             request.headers.setHeader("destination", dst_uri)
+             if depth is not None:
+                 request.headers.setHeader("depth", depth)
+             if overwrite is not None:
+                 request.headers.setHeader("overwrite", overwrite)
+ 
+-            yield (request, do_test)
++            results.append((request, do_test))
+ 
++    yield results
++
++work = deferredGenerator(work)
++
++
+ def sumFile(path):
+     m = md5()
+ 

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_delete.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_delete.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_delete.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,58 @@
+Index: twisted/web2/dav/test/test_delete.py
+===================================================================
+--- twisted/web2/dav/test/test_delete.py	(revision 26969)
++++ twisted/web2/dav/test/test_delete.py	(working copy)
+@@ -26,6 +26,7 @@
+ import urllib
+ import random
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ from twisted.web2 import responsecode
+ from twisted.web2.iweb import IResponse
+ from twisted.web2.test.test_server import SimpleRequest
+@@ -54,8 +55,13 @@
+                 self.fail("DELETE did not remove path %s" % (path,))
+ 
+         def work():
+-            for filename in os.listdir(self.docroot):
+-                path = os.path.join(self.docroot, filename)
++            docroot = waitForDeferred(self.docroot)
++            yield docroot
++            docroot = docroot.getResult()
++            results = []
++
++            for filename in os.listdir(docroot):
++                path = os.path.join(docroot, filename)
+                 uri = urllib.quote("/" + filename)
+ 
+                 if os.path.isdir(path): uri = uri + "/"
+@@ -63,12 +69,26 @@
+                 def do_test(response, path=path):
+                     return check_result(response, path)
+ 
+-                request = SimpleRequest(self.site, "DELETE", uri)
++                site = waitForDeferred(self.site)
++                yield site
++                site = site.getResult()
++                request = SimpleRequest(site, "DELETE", uri)
+ 
+                 depth = random.choice(("infinity", None))
+                 if depth is not None:
+                     request.headers.setHeader("depth", depth)
+ 
+-                yield (request, do_test)
++                results.append((request, do_test))
+ 
+-        return serialize(self.send, work())
++            yield results
++
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        d = d.getResult( )
++
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult( )
++        yield d

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_mkcol.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_mkcol.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_mkcol.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,81 @@
+Index: twisted/web2/dav/test/test_mkcol.py
+===================================================================
+--- twisted/web2/dav/test/test_mkcol.py	(revision 26969)
++++ twisted/web2/dav/test/test_mkcol.py	(working copy)
+@@ -24,6 +24,7 @@
+ 
+ import os
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ from twisted.web2 import responsecode
+ from twisted.web2.iweb import IResponse
+ from twisted.web2.stream import MemoryStream
+@@ -35,6 +36,7 @@
+     """
+     MKCOL request
+     """
++
+     # FIXME:
+     # Try in nonexistant parent collection.
+     # Try on existing resource.
+@@ -43,7 +45,9 @@
+         """
+         MKCOL request
+         """
+-        path, uri = self.mkdtemp("collection")
++        d = waitForDeferred(self.mkdtemp("collection"))
++        yield d
++        path, uri = d.getResult()
+ 
+         rmdir(path)
+ 
+@@ -56,17 +60,28 @@
+             if not os.path.isdir(path):
+                 self.fail("MKCOL did not create directory %s" % (path,))
+ 
+-        request = SimpleRequest(self.site, "MKCOL", uri)
++        site = waitForDeferred(self.site)
++        yield site
++        site = site.getResult()
+ 
+-        return self.send(request, check_result)
++        request = SimpleRequest(site, "MKCOL", uri)
+ 
++        d = waitForDeferred(self.send(request, check_result))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_MKCOL = deferredGenerator(test_MKCOL)
++
+     def test_MKCOL_invalid_body(self):
+         """
+         MKCOL request with invalid request body
+         (Any body at all is invalid in our implementation; there is no
+         such thing as a valid body.)
+         """
+-        path, uri = self.mkdtemp("collection")
++        d = waitForDeferred(self.mkdtemp("collection"))
++        yield d
++        path, uri = d.getResult()
+ 
+         rmdir(path)
+ 
+@@ -79,7 +94,15 @@
+             if os.path.isdir(path):
+                 self.fail("MKCOL incorrectly created directory %s" % (path,))
+ 
+-        request = SimpleRequest(self.site, "MKCOL", uri)
++        site = waitForDeferred(self.site)
++        yield site
++        site = site.getResult()
++        request = SimpleRequest(site, "MKCOL", uri)
+         request.stream = MemoryStream("This is not a valid MKCOL request body.")
+ 
+-        return self.send(request, check_result)
++        d = waitForDeferred(self.send(request, check_result))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_MKCOL_invalid_body = deferredGenerator(test_MKCOL_invalid_body)

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_move.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_move.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_move.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,103 @@
+Index: twisted/web2/dav/test/test_move.py
+===================================================================
+--- twisted/web2/dav/test/test_move.py	(revision 26969)
++++ twisted/web2/dav/test/test_move.py	(working copy)
+@@ -24,6 +24,7 @@
+ 
+ import os
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ import twisted.web2.dav.test.util
+ import twisted.web2.dav.test.test_copy
+ from twisted.web2 import responsecode
+@@ -60,8 +61,21 @@
+                 if sum != sumFile(dst_path):
+                     self.fail("isdir %s produced different directory" % (uri,))
+ 
+-        return serialize(self.send, work(self, test))
++        docroot = waitForDeferred(self.docroot)
++        yield docroot
++        docroot = docroot.getResult()
+ 
++        d = waitForDeferred(work(self, test))
++        yield d
++        d = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult( )
++        yield d
++
++    test_MOVE_create = deferredGenerator(test_MOVE_create)
++
+     def test_MOVE_exists(self):
+         """
+         MOVE to existing resource.
+@@ -74,8 +88,21 @@
+                 # FIXME: Check XML error code (2518bis)
+                 pass
+ 
+-        return serialize(self.send, work(self, test, overwrite=False))
++        docroot = waitForDeferred(self.docroot)
++        yield docroot
++        docroot = docroot.getResult()
+ 
++        d = waitForDeferred(work(self, test, overwrite=False))
++        yield d
++        d = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult( )
++        yield d
++
++    test_MOVE_exists = deferredGenerator(test_MOVE_exists)
++
+     def test_MOVE_overwrite(self):
+         """
+         MOVE to existing resource with overwrite header.
+@@ -88,8 +115,21 @@
+                 # FIXME: Check XML error code (2518bis)
+                 pass
+ 
+-        return serialize(self.send, work(self, test, overwrite=True))
++        docroot = waitForDeferred(self.docroot)
++        yield docroot
++        docroot = docroot.getResult()
+ 
++        d = waitForDeferred(work(self, test, overwrite=True))
++        yield d
++        d = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult( )
++        yield d
++
++    test_MOVE_overwrite = deferredGenerator(test_MOVE_overwrite)
++
+     def test_MOVE_no_parent(self):
+         """
+         MOVE to resource with no parent.
+@@ -102,7 +142,20 @@
+                 # FIXME: Check XML error code (2518bis)
+                 pass
+ 
+-        return serialize(self.send, work(self, test, dst=os.path.join(self.docroot, "elvislives!")))
++        docroot = waitForDeferred(self.docroot)
++        yield docroot
++        docroot = docroot.getResult()
+ 
++        d = waitForDeferred(work(self, test, dst=os.path.join(docroot, "elvislives!")))
++        yield d
++        d = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(d)))
++        yield d
++        d = d.getResult( )
++        yield d
++
++    test_MOVE_no_parent = deferredGenerator(test_MOVE_no_parent)
++
+ def work(self, test, overwrite=None, dst=None):
+     return twisted.web2.dav.test.test_copy.work(self, test, overwrite, dst, depths=(None,))

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_options.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_options.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_options.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,21 @@
+Index: twisted/web2/dav/test/test_options.py
+===================================================================
+--- twisted/web2/dav/test/test_options.py	(revision 26969)
++++ twisted/web2/dav/test/test_options.py	(working copy)
+@@ -31,6 +31,16 @@
+     """
+     OPTIONS request
+     """
++    def setUp(self):
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++        twisted.web2.dav.test.util.TestCase.setUp(self)
++        self._getSite()
++
++
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
+     def test_DAV1(self):
+         """
+         DAV level 1

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_prop.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,28 @@
+Index: twisted/web2/dav/test/test_prop.py
+===================================================================
+--- twisted/web2/dav/test/test_prop.py	(revision 26969)
++++ twisted/web2/dav/test/test_prop.py	(working copy)
+@@ -26,6 +26,7 @@
+ 
+ import random
+ 
++from twisted.internet.defer import deferredGenerator, waitForDeferred
+ from twisted.trial.unittest import SkipTest
+ from twisted.web2 import responsecode
+ from twisted.web2.iweb import IResponse
+@@ -57,6 +58,15 @@
+     PROPFIND, PROPPATCH requests
+     """
+ 
++    def setUp(self):
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++        twisted.web2.dav.test.util.TestCase.setUp(self)
++        self._getSite()
++
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
+     def liveProperties(self):
+         return [lookupElement(qname)() for qname in self.resource_class.liveProperties if (qname[0] == dav_namespace) and qname not in dynamicLiveProperties]
+ 

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_put.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_put.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_put.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,208 @@
+Index: twisted/web2/dav/test/test_put.py
+===================================================================
+--- twisted/web2/dav/test/test_put.py	(revision 26969)
++++ twisted/web2/dav/test/test_put.py	(working copy)
+@@ -25,6 +25,7 @@
+ import os
+ import filecmp
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ from twisted.web2 import responsecode
+ from twisted.web2.iweb import IResponse
+ from twisted.web2.stream import FileStream
+@@ -42,27 +43,38 @@
+         """
+         PUT request
+         """
+-        dst_path = os.path.join(self.docroot, "dst")
++        docroot = waitForDeferred(self.docroot)
++        yield docroot
++        docroot = docroot.getResult()
+ 
+-        def checkResult(response, path):
+-            response = IResponse(response)
++        dst_path = os.path.join(docroot, "dst")
+ 
+-            if response.code not in (
+-                responsecode.CREATED,
+-                responsecode.NO_CONTENT
+-            ):
+-                self.fail("PUT failed: %s" % (response.code,))
++        def makeClosure(path):
+ 
+-            if not os.path.isfile(dst_path):
+-                self.fail("PUT failed to create file %s." % (dst_path,))
++            # Return a function with 'path' closed
+ 
+-            if not filecmp.cmp(path, dst_path):
+-                self.fail("PUT failed to preserve data for file %s in file %s." % (path, dst_path))
++            def checkResult(response):
++                response = IResponse(response)
+ 
+-            etag = response.headers.getHeader("etag")
+-            if not etag:
+-                self.fail("No etag header in PUT response %r." % (response,))
++                if response.code not in (
++                    responsecode.CREATED,
++                    responsecode.NO_CONTENT
++                ):
++                    self.fail("PUT failed: %s" % (response.code,))
+ 
++                if not os.path.isfile(dst_path):
++                    self.fail("PUT failed to create file %s." % (dst_path,))
++
++                if not filecmp.cmp(path, dst_path):
++                    import pdb; pdb.set_trace()
++
++                    self.fail("PUT failed to preserve data for file %s in file %s." % (path, dst_path))
++
++                etag = response.headers.getHeader("etag")
++                if not etag:
++                    self.fail("No etag header in PUT response %r." % (response,))
++            return checkResult
++
+         #
+         # We need to serialize these request & test iterations because they can
+         # interfere with each other.
+@@ -70,52 +82,79 @@
+         def work():
+             dst_uri = "/dst"
+ 
+-            for name in os.listdir(self.docroot):
++            results = []
++            for name in os.listdir(docroot):
+                 if name == "dst":
+                     continue
+ 
+-                path = os.path.join(self.docroot, name)
++                path = os.path.join(docroot, name)
+ 
+                 # Can't really PUT something you can't read
+                 if not os.path.isfile(path): continue
+-    
+-                def do_test(response): checkResult(response, path)
+-    
+-                request = SimpleRequest(self.site, "PUT", dst_uri)
++
++                site = waitForDeferred(self.site)
++                yield site
++                site = site.getResult()
++
++                request = SimpleRequest(site, "PUT", dst_uri)
+                 request.stream = FileStream(file(path, "rb"))
+-    
+-                yield (request, do_test)
+ 
+-        return serialize(self.send, work())
++                results.append((request, makeClosure(path)))
+ 
++            yield results
++
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_PUT_simple = deferredGenerator(test_PUT_simple)
++
++
+     def test_PUT_again(self):
+         """
+         PUT on existing resource with If-None-Match header
+         """
+-        dst_path = os.path.join(self.docroot, "dst")
++        docroot = waitForDeferred(self.docroot)
++        yield docroot
++        docroot = docroot.getResult()
++
++        dst_path = os.path.join(docroot, "dst")
+         dst_uri = "/dst"
+ 
+         def work():
++            results = []
+             for code in (
+                 responsecode.CREATED,
+                 responsecode.PRECONDITION_FAILED,
+                 responsecode.NO_CONTENT,
+                 responsecode.PRECONDITION_FAILED,
+                 responsecode.NO_CONTENT,
+-                responsecode.CREATED,
+             ):
+-                def checkResult(response, code=code):
+-                    response = IResponse(response)
++                def makeClosure(code):
++                    def checkResult(response, code=code):
++                        response = IResponse(response)
+ 
+-                    if response.code != code:
+-                        self.fail("Incorrect response code for PUT (%s != %s)"
+-                                  % (response.code, code))
++                        if response.code != code:
++                            self.fail("Incorrect response code for PUT (%s != %s)"
++                                      % (response.code, code))
++                    return checkResult
+ 
+                 def onError(f):
+                     f.trap(HTTPError)
+                     return checkResult(f.value.response)
+ 
+-                request = SimpleRequest(self.site, "PUT", dst_uri)
++                site = waitForDeferred(self.site)
++                yield site
++                site = site.getResult()
++
++                request = SimpleRequest(site, "PUT", dst_uri)
+                 request.stream = FileStream(file(__file__, "rb"))
+     
+                 if code == responsecode.CREATED:
+@@ -125,10 +164,23 @@
+                 elif code == responsecode.PRECONDITION_FAILED:
+                     request.headers.setHeader("if-none-match", ("*",))
+     
+-                yield (request, (checkResult, onError))
++                results.append((request, (makeClosure(code), onError)))
+ 
+-        return serialize(self.send, work())
++            yield results
+ 
++        work = deferredGenerator(work)
++
++        d = waitForDeferred(work())
++        yield d
++        results = d.getResult()
++
++        d = waitForDeferred(serialize(self.send, iter(results)))
++        yield d
++        d = d.getResult()
++        yield d
++
++    test_PUT_again = deferredGenerator(test_PUT_again)
++
+     def test_PUT_no_parent(self):
+         """
+         PUT with no parent
+@@ -142,7 +194,16 @@
+                 self.fail("Incorrect response code for PUT with no parent (%s != %s)"
+                           % (response.code, responsecode.CONFLICT))
+ 
+-        request = SimpleRequest(self.site, "PUT", dst_uri)
++        site = waitForDeferred(self.site)
++        yield site
++        site = site.getResult()
++
++        request = SimpleRequest(site, "PUT", dst_uri)
+         request.stream = FileStream(file(__file__, "rb"))
+ 
+-        return self.send(request, checkResult)
++        result = waitForDeferred(self.send(request, checkResult))
++        yield result
++        result = result.getResult()
++        yield result
++
++    test_PUT_no_parent = deferredGenerator(test_PUT_no_parent)

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_quota.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,49 @@
+Index: twisted/web2/dav/test/test_quota.py
+===================================================================
+--- twisted/web2/dav/test/test_quota.py	(revision 26969)
++++ twisted/web2/dav/test/test_quota.py	(working copy)
+@@ -22,6 +22,7 @@
+ # DRI: Wilfredo Sanchez, wsanchez at apple.com
+ ##
+ 
++from twisted.internet.defer import waitForDeferred, deferredGenerator
+ from twisted.web2 import responsecode
+ from twisted.web2.iweb import IResponse
+ from twisted.web2.stream import FileStream
+@@ -34,16 +35,31 @@
+ 
+ class QuotaBase(twisted.web2.dav.test.util.TestCase):
+ 
+-    def createDocumentRoot(self):
++    def setUp(self):
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++
++        twisted.web2.dav.test.util.TestCase.setUp(self)
++
+         docroot = self.mktemp()
+         os.mkdir(docroot)
+         rootresource = self.resource_class(docroot)
+-        rootresource.setAccessControlList(self.grantInherit(davxml.All()))
+-        self.site = Site(rootresource)
+-        self.site.resource.setQuotaRoot(None, 100000)
+-        return docroot
++        d = waitForDeferred(rootresource.setAccessControlList(self.grantInherit(davxml.All())))
++        yield d
++        d = d.getResult()
+ 
++        self._site = Site(rootresource)
++        d = waitForDeferred(self._site.resource.setQuotaRoot(None, 100000))
++        yield d
++        d = d.getResult()
++        yield d
+ 
++    setUp = deferredGenerator(setUp)
++
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
++
+     def checkQuota(self, value):
+         def _defer(quota):
+             self.assertEqual(quota, value)

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_report.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_report.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_report.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,21 @@
+Index: twisted/web2/dav/test/test_report.py
+===================================================================
+--- twisted/web2/dav/test/test_report.py	(revision 26969)
++++ twisted/web2/dav/test/test_report.py	(working copy)
+@@ -34,6 +34,16 @@
+     """
+     REPORT request
+     """
++
++    def setUp(self):
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++        twisted.web2.dav.test.util.TestCase.setUp(self)
++        self._getSite()
++
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
+     def test_REPORT_no_body(self):
+         """
+         REPORT request with no body

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,7 +1,40 @@
-=== modified file 'twisted/web2/dav/test/test_resource.py'
---- twisted/web2/dav/test/test_resource.py	2009-05-07 20:35:07 +0000
-+++ twisted/web2/dav/test/test_resource.py	2009-07-15 23:30:18 +0000
-@@ -376,7 +376,7 @@
+Index: twisted/web2/dav/test/test_resource.py
+===================================================================
+--- twisted/web2/dav/test/test_resource.py	(revision 26969)
++++ twisted/web2/dav/test/test_resource.py	(working copy)
+@@ -39,7 +39,13 @@
+     def setUp(self):
+         twisted.web2.dav.test.util.TestCase.setUp(self)
+         TestResource._cachedPropertyStores = {}
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++        self._getSite()
+ 
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
+ class GenericDAVResource(TestCase):
+     def setUp(self):
+         TestCase.setUp(self)
+@@ -60,7 +66,7 @@
+             })
+         })
+ 
+-        self.site = Site(rootresource)
++        self._site = Site(rootresource)
+ 
+     def test_findChildren(self):
+         """
+@@ -234,7 +240,7 @@
+         loginInterfaces = (IPrincipal,)
+ 
+         self.rootresource = rootresource
+-        self.site = Site(AuthenticationWrapper(
++        self._site = Site(AuthenticationWrapper(
+             self.rootresource,
+             portal,
+             credentialFactories,
+@@ -376,7 +382,7 @@
          return self.children is not None
  
      def listChildren(self):
@@ -10,4 +43,11 @@
  
      def supportedPrivileges(self, request):
          return succeed(davPrivilegeSet)
-
+@@ -398,6 +404,7 @@
+ 
+     def setAccessControlList(self, acl):
+         self.acl = acl
++        return succeed(self.acl)
+ 
+     def accessControlList(self, request, **kwargs):
+         return succeed(self.acl)

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_static.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,21 @@
+Index: twisted/web2/dav/test/test_static.py
+===================================================================
+--- twisted/web2/dav/test/test_static.py	(revision 26969)
++++ twisted/web2/dav/test/test_static.py	(working copy)
+@@ -28,6 +28,16 @@
+ from twisted.web2.test.test_server import SimpleRequest
+ 
+ class DAVFileTest(util.TestCase):
++
++    def setUp(self):
++        # Pre-fetch site, so the rest of the test doesn't have to defer
++        util.TestCase.setUp(self)
++        self._getSite()
++
++    def _getStoredSite(self):
++        return self._site
++    site = property(_getStoredSite)
++
+     def test_renderPrivileges(self):
+         """
+         Verify that a directory listing includes children which you

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_xattrprops.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_xattrprops.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.test_xattrprops.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,170 @@
+Index: twisted/web2/dav/test/test_xattrprops.py
+===================================================================
+--- twisted/web2/dav/test/test_xattrprops.py	(revision 26969)
++++ twisted/web2/dav/test/test_xattrprops.py	(working copy)
+@@ -5,10 +5,13 @@
+ Tests for L{twisted.web2.dav.xattrprops}.
+ """
+ 
++import sys
+ from zlib import compress, decompress
+ from pickle import dumps
+ from cPickle import UnpicklingError
+ 
++from twisted.internet.defer import inlineCallbacks, returnValue
++from twisted.python import failure
+ from twisted.python.filepath import FilePath
+ from twisted.trial.unittest import TestCase
+ from twisted.web2.responsecode import NOT_FOUND, INTERNAL_SERVER_ERROR
+@@ -42,16 +45,18 @@
+         self.propertyStore = xattrPropertyStore(self.resource)
+ 
+ 
++    @inlineCallbacks
+     def test_getAbsent(self):
+         """
+         L{xattrPropertyStore.get} raises L{HTTPError} with a I{NOT FOUND}
+         response code if passed the name of an attribute for which there is no
+         corresponding value.
+         """
+-        error = self.assertRaises(HTTPError, self.propertyStore.get, ("foo", "bar"))
++        error = (yield self.assertRaisesDeferred(HTTPError, self.propertyStore.get, ("foo", "bar")))
+         self.assertEquals(error.response.code, NOT_FOUND)
+ 
+ 
++    @inlineCallbacks
+     def _forbiddenTest(self, method):
+         # Remove access to the directory containing the file so that getting
+         # extended attributes from it fails with EPERM.
+@@ -62,10 +67,10 @@
+ 
+         # Try to get a property from it - and fail.
+         document = self._makeValue()
+-        error = self.assertRaises(
++        error = (yield self.assertRaisesDeferred(
+             HTTPError,
+             getattr(self.propertyStore, method),
+-            document.root_element.qname())
++            document.root_element.qname()))
+ 
+         # Make sure that the status is FORBIDDEN, a roughly reasonable mapping
+         # of the EPERM failure.
+@@ -106,11 +111,12 @@
+         return self.attrs[attribute]
+ 
+ 
++    @inlineCallbacks
+     def _checkValue(self, originalDocument):
+         property = originalDocument.root_element.qname()
+ 
+         # Try to load it via xattrPropertyStore.get
+-        loadedDocument = self.propertyStore.get(property)
++        loadedDocument = (yield self.propertyStore.get(property))
+ 
+         # XXX Why isn't this a WebDAVDocument?
+         self.assertIsInstance(loadedDocument, Depth)
+@@ -178,6 +184,7 @@
+             decompress(self._getValue(document)), document.toxml())
+ 
+ 
++    @inlineCallbacks
+     def test_getInvalid(self):
+         """
+         If the value associated with the property name passed to
+@@ -190,7 +197,7 @@
+             "random garbage goes here! \0 that nul is definitely garbage")
+ 
+         property = document.root_element.qname()
+-        error = self.assertRaises(HTTPError, self.propertyStore.get, property)
++        error = (yield self.assertRaisesDeferred(HTTPError, self.propertyStore.get, property))
+         self.assertEquals(error.response.code, INTERNAL_SERVER_ERROR)
+         self.assertEquals(
+             len(self.flushLoggedErrors(UnpicklingError)), 1)
+@@ -227,6 +234,7 @@
+         self.assertRaises(KeyError, self._getValue, document)
+ 
+ 
++    @inlineCallbacks
+     def test_deleteErrors(self):
+         """
+         If there is a problem deleting the specified property (aside from the
+@@ -240,15 +248,16 @@
+ 
+         # Try to delete a property from it - and fail.
+         document = self._makeValue()
+-        error = self.assertRaises(
++        error = (yield self.assertRaisesDeferred(
+             HTTPError,
+-            self.propertyStore.delete, document.root_element.qname())
++            self.propertyStore.delete, document.root_element.qname()))
+ 
+         # Make sure that the status is NOT FOUND, a roughly reasonable mapping
+         # of the EEXIST failure.
+         self.assertEquals(error.response.code, NOT_FOUND)
+ 
+ 
++    @inlineCallbacks
+     def test_contains(self):
+         """
+         L{xattrPropertyStore.contains} returns C{True} if the given property
+@@ -256,10 +265,10 @@
+         """
+         document = self._makeValue()
+         self.assertFalse(
+-            self.propertyStore.contains(document.root_element.qname()))
++            (yield self.propertyStore.contains(document.root_element.qname())))
+         self._setValue(document, document.toxml())
+         self.assertTrue(
+-            self.propertyStore.contains(document.root_element.qname()))
++            (yield self.propertyStore.contains(document.root_element.qname())))
+ 
+ 
+     def test_containsError(self):
+@@ -272,6 +281,7 @@
+         self._forbiddenTest('contains')
+ 
+ 
++    @inlineCallbacks
+     def test_list(self):
+         """
+         L{xattrPropertyStore.list} returns a C{list} of property names
+@@ -281,10 +291,11 @@
+         self.attrs[prefix + '{foo}bar'] = 'baz'
+         self.attrs[prefix + '{bar}baz'] = 'quux'
+         self.assertEquals(
+-            set(self.propertyStore.list()),
++            set((yield self.propertyStore.list())),
+             set([(u'foo', u'bar'), (u'bar', u'baz')]))
+ 
+ 
++    @inlineCallbacks
+     def test_listError(self):
+         """
+         If there is a problem checking if the specified property exists (aside
+@@ -301,8 +312,25 @@
+ 
+         # Try to get a property from it - and fail.
+         document = self._makeValue()
+-        error = self.assertRaises(HTTPError, self.propertyStore.list)
++        error = (yield self.assertRaisesDeferred(HTTPError, self.propertyStore.list))
+ 
+         # Make sure that the status is FORBIDDEN, a roughly reasonable mapping
+         # of the EPERM failure.
+         self.assertEquals(error.response.code, FORBIDDEN)
++
++
++    @inlineCallbacks
++    def assertRaisesDeferred(self, exception, f, *args, **kwargs):
++        try:
++            result = (yield f(*args, **kwargs))
++        except exception, inst:
++            returnValue(inst)
++        except:
++            raise self.failureException('%s raised instead of %s:\n %s'
++                                        % (sys.exc_info()[0],
++                                           exception.__name__,
++                                           failure.Failure().getTraceback()))
++        else:
++            raise self.failureException('%s not raised (%r returned)'
++                                        % (exception.__name__, result))
++

Added: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.util.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.util.patch	                        (rev 0)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.test.util.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -0,0 +1,113 @@
+Index: twisted/web2/dav/test/util.py
+===================================================================
+--- twisted/web2/dav/test/util.py	(revision 26969)
++++ twisted/web2/dav/test/util.py	(working copy)
+@@ -31,7 +31,7 @@
+ 
+ from twisted.python import log
+ from twisted.trial import unittest
+-from twisted.internet.defer import Deferred
++from twisted.internet.defer import Deferred, waitForDeferred, deferredGenerator, succeed
+ 
+ from twisted.web2.http import HTTPError, StatusResponse
+ from twisted.web2 import responsecode
+@@ -60,22 +60,24 @@
+             ))
+ 
+         doc = davxml.WebDAVDocument.fromString(property)
+-        return doc.root_element
++        return succeed(doc.root_element)
+ 
+     def set(self, property):
+         self._dict[property.qname()] = property.toxml()
++        return succeed(None)
+ 
+     def delete(self, qname):
+         try:
+             del(self._dict[qname])
+         except KeyError:
+             pass
++        return succeed(None)
+ 
+     def contains(self, qname):
+-        return qname in self._dict
++        return succeed(qname in self._dict)
+ 
+     def list(self):
+-        return self._dict.keys()
++        return succeed(self._dict.keys())
+ 
+ class TestFile (DAVFile):
+     _cachedPropertyStores = {}
+@@ -121,7 +123,9 @@
+         docroot = self.mktemp()
+         os.mkdir(docroot)
+         rootresource = self.resource_class(docroot)
+-        rootresource.setAccessControlList(self.grantInherit(davxml.All()))
++        d = waitForDeferred(rootresource.setAccessControlList(self.grantInherit(davxml.All())))
++        yield d
++        d = d.getResult()
+ 
+         dirnames = (
+             os.path.join(docroot, "dir1"),                          # 0
+@@ -149,17 +153,23 @@
+         for dirname in (docroot,) + dirnames[3:8+1]:
+             for filename in filenames[:5]:
+                 copy(filename, dirname)
+-        return docroot
++        yield docroot
+ 
++    createDocumentRoot = deferredGenerator(createDocumentRoot)
+ 
++
+     def _getDocumentRoot(self):
+         if not hasattr(self, "_docroot"):
+             log.msg("Setting up docroot for %s" % (self.__class__,))
+ 
+-            self._docroot = self.createDocumentRoot()
++            d = waitForDeferred(self.createDocumentRoot())
++            yield d
++            self._docroot = d.getResult()
+ 
+-        return self._docroot
++        yield self._docroot
+ 
++    _getDocumentRoot = deferredGenerator(_getDocumentRoot)
++
+     def _setDocumentRoot(self, value):
+         self._docroot = value
+ 
+@@ -167,10 +177,13 @@
+ 
+     def _getSite(self):
+         if not hasattr(self, "_site"):
+-            rootresource = self.resource_class(self.docroot)
++            d = waitForDeferred(self.docroot)
++            yield d
++            rootresource = self.resource_class(d.getResult())
+             rootresource.setAccessControlList(self.grantInherit(davxml.All()))
+             self._site = Site(rootresource)
+-        return self._site
++        yield self._site
++    _getSite = deferredGenerator(_getSite)
+ 
+     def _setSite(self, site):
+         self._site = site
+@@ -191,11 +204,15 @@
+         Creates a new directory in the document root and returns its path and
+         URI.
+         """
+-        path = mkdtemp(prefix=prefix + "_", dir=self.docroot)
++        d = waitForDeferred(self.docroot)
++        yield d
++        path = mkdtemp(prefix=prefix + "_", dir=d.getResult())
+         uri  = joinURL("/", url_quote(os.path.basename(path))) + "/"
+ 
+-        return (path, uri)
++        yield (path, uri)
+ 
++    mkdtemp = deferredGenerator(mkdtemp)
++
+     def send(self, request, callback):
+         log.msg("Sending %s request for URI %s" % (request.method, request.uri))
+ 

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.dav.xattrprops.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,20 +1,120 @@
-=== modified file 'twisted/web2/dav/xattrprops.py'
---- twisted/web2/dav/xattrprops.py	2009-06-03 23:46:41 +0000
-+++ twisted/web2/dav/xattrprops.py	2009-07-15 23:30:18 +0000
-@@ -49,6 +49,7 @@
+Index: twisted/web2/dav/xattrprops.py
+===================================================================
+--- twisted/web2/dav/xattrprops.py	(revision 26969)
++++ twisted/web2/dav/xattrprops.py	(working copy)
+@@ -47,6 +47,8 @@
+ if getattr(xattr, 'xattr', None) is None:
+     raise ImportError("wrong xattr package imported")
  
++from twisted.internet.defer import inlineCallbacks, returnValue, succeed
++
  from twisted.python.util import untilConcludes
  from twisted.python.failure import Failure
-+from twisted.internet.defer import succeed
  from twisted.python import log
- from twisted.web2 import responsecode
- from twisted.web2.http import HTTPError, StatusResponse
-@@ -188,6 +189,7 @@
+@@ -124,18 +126,18 @@
+         try:
+             data = self.attrs.get(self._encode(qname))
+         except KeyError:
+-            raise HTTPError(StatusResponse(
++            return Failure(HTTPError(StatusResponse(
+                     responsecode.NOT_FOUND,
+-                    "No such property: {%s}%s" % qname))
++                    "No such property: {%s}%s" % qname)))
+         except IOError, e:
+             if e.errno in _ATTR_MISSING:
+-                raise HTTPError(StatusResponse(
++                return Failure(HTTPError(StatusResponse(
+                         responsecode.NOT_FOUND,
+-                        "No such property: {%s}%s" % qname))
++                        "No such property: {%s}%s" % qname)))
+             else:
+-                raise HTTPError(StatusResponse(
++                return Failure(HTTPError(StatusResponse(
+                         statusForFailure(Failure()),
+-                        "Unable to read property: {%s}%s" % qname))
++                        "Unable to read property: {%s}%s" % qname)))
  
+         #
+         # Unserialize XML data from an xattr.  The storage format has changed
+@@ -165,15 +167,15 @@
+                 format = "Invalid property value stored on server: {%s}%s %s"
+                 msg = format % (qname[0], qname[1], data)
+                 log.err(None, msg)
+-                raise HTTPError(
+-                    StatusResponse(responsecode.INTERNAL_SERVER_ERROR, msg))
++                return Failure (HTTPError(
++                    StatusResponse(responsecode.INTERNAL_SERVER_ERROR, msg)))
+             else:
+                 legacy = True
+ 
+         if legacy:
+             self.set(doc.root_element)
+ 
+-        return doc.root_element
++        return succeed(doc.root_element)
+ 
+ 
+     def set(self, property):
+@@ -188,6 +190,7 @@
+ 
          # Update the resource because we've modified it
          self.resource.fp.restat()
 +        return succeed(None)
  
  
      def delete(self, qname):
-
+@@ -208,10 +211,11 @@
+                 if e.errno not in _ATTR_MISSING:
+                     raise
+         except:
+-            raise HTTPError(
++            return Failure(HTTPError(
+                 StatusResponse(
+                     statusForFailure(Failure()),
+-                    "Unable to delete property: " + key))
++                    "Unable to delete property: " + key)))
++        return succeed(None)
+ 
+ 
+     def contains(self, qname):
+@@ -228,16 +232,16 @@
+         try:
+             self.attrs.get(key)
+         except KeyError:
+-            return False
++            return succeed(False)
+         except IOError, e:
+             if e.errno in _ATTR_MISSING or e.errno == errno.ENOENT:
+-                return False
+-            raise HTTPError(
++                return succeed(False)
++            return Failure(HTTPError(
+                 StatusResponse(
+                     statusForFailure(Failure()),
+-                    "Unable to read property: " + key))
++                    "Unable to read property: " + key)))
+         else:
+-            return True
++            return succeed(True)
+ 
+ 
+     def list(self):
+@@ -252,13 +256,14 @@
+         try:
+             attrs = iter(self.attrs)
+         except IOError, e:
+-            raise HTTPError(
++            return Failure(HTTPError(
+                 StatusResponse(
+                     statusForFailure(Failure()),
+-                    "Unable to list properties: " + self.resource.fp.path))
++                    "Unable to list properties: " + self.resource.fp.path)))
+         else:
+-            return [
+-                self._decode(name)
++            return succeed(
++                [self._decode(name)
+                 for name
+                 in attrs
+                 if name.startswith(prefix)]
++            )

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.error.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.error.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.error.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,6 +1,7 @@
-=== modified file 'twisted/web2/error.py'
---- twisted/web2/error.py	2005-11-22 22:59:59 +0000
-+++ twisted/web2/error.py	2009-07-15 23:30:18 +0000
+Index: twisted/web2/error.py
+===================================================================
+--- twisted/web2/error.py	(revision 26969)
++++ twisted/web2/error.py	(working copy)
 @@ -92,7 +92,7 @@
              "<body><h1>%s</h1>%s</body></html>") % (
          response.code, title, title, message)
@@ -10,4 +11,3 @@
      response.stream = stream.MemoryStream(body)
      
      return response
-

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.server.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.server.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.server.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -28,4 +28,3 @@
          # This is where CONNECT would go if we wanted it
          return None
  
-

Modified: CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.static.patch
===================================================================
--- CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.static.patch	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/lib-patches/Twisted/twisted.web2.static.patch	2009-08-26 17:42:05 UTC (rev 4508)
@@ -1,16 +1,88 @@
-=== modified file 'twisted/web2/static.py'
---- twisted/web2/static.py	2009-04-20 17:38:06 +0000
-+++ twisted/web2/static.py	2009-07-15 23:30:18 +0000
+Index: twisted/web2/static.py
+===================================================================
+--- twisted/web2/static.py	(revision 26969)
++++ twisted/web2/static.py	(working copy)
 @@ -16,7 +16,7 @@
  
  # Twisted Imports
  from twisted.python import filepath
 -from twisted.internet.defer import maybeDeferred
-+from twisted.internet.defer import maybeDeferred, succeed
++from twisted.internet.defer import maybeDeferred, succeed, deferredGenerator, waitForDeferred
  from zope.interface import implements
  
  class MetaDataMixin(object):
-@@ -291,12 +291,13 @@
+@@ -28,7 +28,7 @@
+         """
+         @return: The current etag for the resource if available, None otherwise.
+         """
+-        return None
++        return succeed(None)
+ 
+     def lastModified(self):
+         """
+@@ -76,18 +76,26 @@
+     def checkPreconditions(self, request):
+         # This code replaces the code in resource.RenderMixin
+         if request.method not in ("GET", "HEAD"):
++            etag = waitForDeferred(self.etag())
++            yield etag
++            etag = etag.getResult()
+             http.checkPreconditions(
+                 request,
+                 entityExists = self.exists(),
+-                etag = self.etag(),
++                etag = etag,
+                 lastModified = self.lastModified(),
+             )
+ 
+         # Check per-method preconditions
+         method = getattr(self, "preconditions_" + request.method, None)
+         if method:
+-            return method(request)
++            result = waitForDeferred(method(request))
++            yield result
++            result = result.getResult()
++            yield result
+ 
++    checkPreconditions = deferredGenerator(checkPreconditions)
++
+     def renderHTTP(self, request):
+         """
+         See L{resource.RenderMixIn.renderHTTP}.
+@@ -132,8 +140,8 @@
+ 
+     def etag(self):
+         lastModified = self.lastModified()
+-        return http_headers.ETag("%X-%X" % (lastModified, hash(self.data)),
+-                                 weak=(time.time() - lastModified <= 1))
++        return succeed(http_headers.ETag("%X-%X" % (lastModified, hash(self.data)),
++                                 weak=(time.time() - lastModified <= 1)))
+ 
+     def lastModified(self):
+         return self.creationDate()
+@@ -217,7 +225,7 @@
+         return self.fp.exists()
+ 
+     def etag(self):
+-        if not self.fp.exists(): return None
++        if not self.fp.exists(): return succeed(None)
+ 
+         st = self.fp.statinfo
+ 
+@@ -228,10 +236,10 @@
+         #
+         weak = (time.time() - st.st_mtime <= 1)
+ 
+-        return http_headers.ETag(
++        return succeed(http_headers.ETag(
+             "%X-%X-%X" % (st.st_ino, st.st_size, st.st_mtime),
+             weak=weak
+-        )
++        ))
+ 
+     def lastModified(self):
+         if self.fp.exists():
+@@ -291,12 +299,13 @@
          self.ignoredExts.append(ext)
  
      def directoryListing(self):
@@ -30,7 +102,7 @@
      def putChild(self, name, child):
          """
          Register a child with the given name with this resource.
-@@ -329,7 +330,7 @@
+@@ -329,7 +338,7 @@
          children = self.putChildren.keys()
          if self.fp.isdir():
              children += [c for c in self.fp.listdir() if c not in children]
@@ -39,7 +111,7 @@
  
      def locateChild(self, req, segments):
          """
-@@ -387,17 +388,17 @@
+@@ -387,17 +396,17 @@
                  ifp = self.fp.childSearchPreauth(*self.indexNames)
                  if ifp:
                      # Render from the index file
@@ -66,4 +138,3 @@
  
          try:
              f = self.fp.open()
-

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/directory/calendar.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/directory/calendar.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/directory/calendar.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -299,9 +299,10 @@
         if hasattr(self, "clientNotifier"):
             self.clientNotifier.disableNotify()
 
+        @inlineCallbacks
         def setupFreeBusy(_, child):
             # Default calendar is initially opaque to freebusy
-            child.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
+            yield child.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()))
 
             # FIXME: Shouldn't have to call provision() on another resource
             # We cheat here because while inbox will auto-provision itself when located,
@@ -314,9 +315,9 @@
             inbox.processFreeBusyCalendar(childURL, True)
 
             # Default calendar is marked as the default for scheduling
-            inbox.writeDeadProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(childURL)))
+            yield inbox.writeDeadProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(childURL)))
 
-            return self
+            returnValue(self)
 
         try:
             d = self.provision()

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/mail.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/mail.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/mail.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -245,7 +245,7 @@
         return None
 
     def checkPreconditions(self, request):
-        return None
+        return succeed(None)
 
     def render(self, request):
         output = """<html>

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/memcacheprops.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/memcacheprops.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/memcacheprops.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -37,6 +37,7 @@
 from twisted.python.filepath import FilePath
 from twisted.web2 import responsecode
 from twisted.web2.http import HTTPError, StatusResponse
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 
 from twistedcaldav.config import config
 from twistedcaldav.log import LoggingMixIn, Logger
@@ -68,6 +69,7 @@
 
         return MemcachePropertyCollection._memcacheClient
 
+    @inlineCallbacks
     def propertyCache(self):
         # The property cache has this format:
         #  {
@@ -82,13 +84,14 @@
         #    ...,
         #  }
         if not hasattr(self, "_propertyCache"):
-            self._propertyCache = self._loadCache()
-        return self._propertyCache
+            self._propertyCache = (yield self._loadCache())
+        returnValue(self._propertyCache)
 
+    @inlineCallbacks
     def childCache(self, child):
         path = child.fp.path
         key = self._keyForPath(path)
-        propertyCache = self.propertyCache()
+        propertyCache = (yield self.propertyCache())
 
         try:
             childCache, token = propertyCache[key]
@@ -100,7 +103,7 @@
             #log.error(message)
             #raise AssertionError(message)
 
-        return propertyCache, key, childCache, token
+        returnValue((propertyCache, key, childCache, token))
 
     def _keyForPath(self, path):
         key = "|".join((
@@ -109,6 +112,7 @@
         ))
         return md5(key).hexdigest()
 
+    @inlineCallbacks
     def _loadCache(self, childNames=None):
         if childNames is None:
             abortIfMissing = False
@@ -117,14 +121,14 @@
             if childNames:
                 abortIfMissing = True
             else:
-                return {}
+                returnValue({})
 
         self.log_debug("Loading cache for %s" % (self.collection,))
 
         client = self.memcacheClient()
         assert client is not None, "OMG no cache!"
         if client is None:
-            return None
+            returnValue(None)
 
         keys = tuple((
             (self._keyForPath(self.collection.fp.child(childName).path), childName)
@@ -153,12 +157,12 @@
             if abortIfMissing:
                 raise MemcacheError("Unable to fully load cache for %s" % (self.collection,))
 
-            loaded = self._buildCache(childNames=missing)
-            loaded = self._loadCache(childNames=(FilePath(name).basename() for name in loaded.iterkeys()))
+            loaded = (yield self._buildCache(childNames=missing))
+            loaded = (yield self._loadCache(childNames=(FilePath(name).basename() for name in loaded.iterkeys())))
 
             result.update(loaded.iteritems())
 
-        return result
+        returnValue(result)
 
     def _storeCache(self, cache):
         self.log_debug("Storing cache for %s" % (self.collection,))
@@ -173,36 +177,33 @@
         if client is not None:
             client.set_multi(values, time=self.cacheTimeout)
 
+    @inlineCallbacks
     def _buildCache(self, childNames=None):
         if childNames is None:
             childNames = self.collection.listChildren()
         elif not childNames:
-            return {}
+            returnValue({})
 
         self.log_debug("Building cache for %s" % (self.collection,))
 
         cache = {}
         ds = []
         for childName in childNames:
-            d = self.collection.getChild(childName)
-            def _gotChild(child):
-                if child is not None:
-                    propertyStore = child.deadProperties()
-                    props = {}
-                    for qname in propertyStore.list(cache=False):
-                        props[qname] = propertyStore.get(qname, cache=False)
+            child = (yield self.collection.getChild(childName))
+            if child is not None:
+                propertyStore = child.deadProperties()
+                props = {}
+                for qname in (yield propertyStore.list(cache=False)):
+                    props[qname] = (yield propertyStore.get(qname, cache=False))
 
-                    cache[child.fp.path] = props
-            d.addCallback(_gotChild)
-            ds.append(d)
-        def _collectedChildren(_):
-            self._storeCache(cache)
-            return cache
-        return DeferredList(ds).addCallback(_collectedChildren)
+                cache[child.fp.path] = props
+        self._storeCache(cache)
+        returnValue(cache)
 
 
+    @inlineCallbacks
     def setProperty(self, child, property, delete=False):
-        propertyCache, key, childCache, token = self.childCache(child)
+        propertyCache, key, childCache, token = (yield self.childCache(child))
 
         if delete:
             qname = property
@@ -230,12 +231,12 @@
 
                 finally:
                     # Re-fetch the properties for this child
-                    loaded = self._loadCache(childNames=(child.fp.basename(),))
+                    loaded = (yield self._loadCache(childNames=(child.fp.basename(),)))
                     propertyCache.update(loaded.iteritems())
 
                 retries -= 1
 
-                propertyCache, key, childCache, token = self.childCache(child)
+                propertyCache, key, childCache, token = (yield self.childCache(child))
 
                 if delete:
                     if childCache.has_key(qname):
@@ -253,10 +254,11 @@
     def deleteProperty(self, child, qname):
         return self.setProperty(child, qname, delete=True)
 
+    @inlineCallbacks
     def flushCache(self, child):
         path = child.fp.path
         key = self._keyForPath(path)
-        propertyCache = self.propertyCache()
+        propertyCache = (yield self.propertyCache())
 
         if key in propertyCache:
             del propertyCache[key]
@@ -276,20 +278,22 @@
             self.child = child
             self.childPropertyStore = childPropertyStore
 
+        @inlineCallbacks
         def propertyCache(self):
             path = self.child.fp.path
             key = self.parentPropertyCollection._keyForPath(path)
-            parentPropertyCache = self.parentPropertyCollection.propertyCache()
-            return parentPropertyCache.get(key, ({}, None))[0]
+            parentPropertyCache = (yield self.parentPropertyCollection.propertyCache())
+            returnValue(parentPropertyCache.get(key, ({}, None))[0])
 
         def flushCache(self):
             self.parentPropertyCollection.flushCache(self.child)
 
+        @inlineCallbacks
         def get(self, qname, cache=True):
             if cache:
-                propertyCache = self.propertyCache()
+                propertyCache = (yield self.propertyCache())
                 if qname in propertyCache:
-                    return propertyCache[qname]
+                    returnValue(propertyCache[qname])
                 else:
                     raise HTTPError(StatusResponse(
                         responsecode.NOT_FOUND,
@@ -298,7 +302,7 @@
 
             self.log_debug("Read for %s on %s"
                            % (qname, self.childPropertyStore.resource.fp.path))
-            return self.childPropertyStore.get(qname)
+            returnValue((yield self.childPropertyStore.get(qname)))
 
         def set(self, property):
             self.log_debug("Write for %s on %s"
@@ -306,6 +310,7 @@
 
             self.parentPropertyCollection.setProperty(self.child, property)
             self.childPropertyStore.set(property)
+            return succeed(None)
 
         def delete(self, qname):
             self.log_debug("Delete for %s on %s"
@@ -313,21 +318,24 @@
 
             self.parentPropertyCollection.deleteProperty(self.child, qname)
             self.childPropertyStore.delete(qname)
+            return succeed(None)
 
+        @inlineCallbacks
         def contains(self, qname, cache=True):
             if cache:
-                propertyCache = self.propertyCache()
-                return qname in propertyCache
+                propertyCache = (yield self.propertyCache())
+                returnValue(qname in propertyCache)
 
             self.log_debug("Contains for %s"
                            % (self.childPropertyStore.resource.fp.path,))
-            return self.childPropertyStore.contains(qname)
+            returnValue(self.childPropertyStore.contains(qname))
 
+        @inlineCallbacks
         def list(self, cache=True):
             if cache:
-                propertyCache = self.propertyCache()
-                return propertyCache.iterkeys()
+                propertyCache = (yield self.propertyCache())
+                returnValue(propertyCache.iterkeys())
 
             self.log_debug("List for %s"
                            % (self.childPropertyStore.resource.fp.path,))
-            return self.childPropertyStore.list()
+            returnValue(self.childPropertyStore.list())

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/method/delete_common.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/method/delete_common.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/method/delete_common.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -52,6 +52,7 @@
         self.depth = depth
         self.internal_request = internal_request
 
+    @inlineCallbacks
     def validIfScheduleMatch(self):
         """
         Check for If-ScheduleTag-Match header behavior.
@@ -63,8 +64,8 @@
             if header:
                 # Do "precondition" test
                 matched = False
-                if self.resource.exists() and self.resource.hasDeadProperty(ScheduleTag):
-                    scheduletag = self.resource.readDeadProperty(ScheduleTag)
+                if self.resource.exists() and (yield self.resource.hasDeadProperty(ScheduleTag)):
+                    scheduletag = (yield self.resource.readDeadProperty(ScheduleTag))
                     matched = (scheduletag == header)
                 if not matched:
                     log.debug("If-Schedule-Tag-Match: header value '%s' does not match resource value '%s'" % (header, scheduletag,))
@@ -130,7 +131,7 @@
         # as the iTIP operation may fail and may need to prevent the delete from happening.
     
         # Do If-Schedule-Tag-Match behavior first
-        self.validIfScheduleMatch()
+        yield self.validIfScheduleMatch()
 
         # Do quota checks before we start deleting things
         myquota = (yield delresource.quota(self.request))

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/method/get.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/method/get.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/method/get.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -38,7 +38,7 @@
     # Look for calendar access restriction on existing resource.
     if self.exists():
         try:
-            access = self.readDeadProperty(TwistedCalendarAccessProperty)
+            access = (yield self.readDeadProperty(TwistedCalendarAccessProperty))
         except HTTPError:
             access = None
             
@@ -65,8 +65,8 @@
     response = (yield super(CalDAVFile, self).http_GET(request))
     
     # Add Schedule-Tag header if property is present
-    if self.exists() and self.hasDeadProperty(ScheduleTag):
-        scheduletag = self.readDeadProperty(ScheduleTag)
+    if self.exists() and (yield self.hasDeadProperty(ScheduleTag)):
+        scheduletag = (yield self.readDeadProperty(ScheduleTag))
         if scheduletag:
             response.headers.setHeader("Schedule-Tag", str(scheduletag))
 

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/method/mkcalendar.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/method/mkcalendar.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/method/mkcalendar.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -94,7 +94,7 @@
             for property in makecalendar.children[0].children[0].children:
                 try:
                     if property.qname() == (caldavxml.caldav_namespace, "supported-calendar-component-set"):
-                        self.writeDeadProperty(property)
+                        (yield self.writeDeadProperty(property))
                     else:
                         yield self.writeProperty(property, request)
                 except HTTPError:

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/method/put_common.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/method/put_common.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/method/put_common.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -281,7 +281,7 @@
 
         # Basic validation
         yield self.validCopyMoveOperation()
-        self.validIfScheduleMatch()
+        yield self.validIfScheduleMatch()
 
         if self.destinationcal:
             # Valid resource name check
@@ -393,6 +393,7 @@
                     log.debug(msg)
                     raise HTTPError(StatusResponse(responsecode.FORBIDDEN, msg))
 
+    @inlineCallbacks
     def validIfScheduleMatch(self):
         """
         Check for If-ScheduleTag-Match header behavior.
@@ -404,12 +405,14 @@
             header = self.request.headers.getHeader("If-Schedule-Tag-Match")
             if header:
                 # Do "precondition" test
-                
+
                 # If COPY/MOVE get Schedule-Tag on source, else use destination
-                def _getScheduleTag(resource):
-                    return resource.readDeadProperty(ScheduleTag) if resource.exists() and resource.hasDeadProperty(ScheduleTag) else None
+                resource = self.source if self.source else self.destination
+                if resource.exists() and (yield resource.hasDeadProperty(ScheduleTag)):
+                    scheduletag = (yield resource.readDeadProperty(ScheduleTag))
+                else:
+                    scheduletag = None
 
-                scheduletag = _getScheduleTag(self.source if self.source else self.destination)
                 if scheduletag != header:
                     log.debug("If-Schedule-Tag-Match: header value '%s' does not match resource value '%s'" % (header, scheduletag,))
                     raise HTTPError(responsecode.PRECONDITION_FAILED)
@@ -539,6 +542,7 @@
 
         return result, message
 
+    @inlineCallbacks
     def validAccess(self):
         """
         Make sure that the X-CALENDARSERVER-ACCESS property is properly dealt with.
@@ -553,25 +557,19 @@
                 
             # Only DAV:owner is able to set the property to other than PUBLIC
             if not self.internal_request:
-                def _callback(parent_owner):
-                    
-                    authz = self.destinationparent.currentPrincipal(self.request)
-                    if davxml.Principal(parent_owner) != authz and self.access != Component.ACCESS_PUBLIC:
-                        raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (calendarserver_namespace, "valid-access-restriction-change")))
-                    
-                    return None
-    
-                d = self.destinationparent.owner(self.request)
-                d.addCallback(_callback)
-                return d
+                parentOwner = (yield self.destinationparent.owner(self.request))
+                authz = self.destinationparent.currentPrincipal(self.request)
+                if davxml.Principal(parentOwner) != authz and self.access != Component.ACCESS_PUBLIC:
+                    raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (calendarserver_namespace, "valid-access-restriction-change")))
+
         else:
             # Check whether an access property was present before and write that into the calendar data
-            if not self.source and self.destination.exists() and self.destination.hasDeadProperty(TwistedCalendarAccessProperty):
-                old_access = str(self.destination.readDeadProperty(TwistedCalendarAccessProperty))
+            if not self.source and self.destination.exists() and (yield self.destination.hasDeadProperty(TwistedCalendarAccessProperty)):
+                old_access = str((yield self.destination.readDeadProperty(TwistedCalendarAccessProperty)))
                 self.calendar.addProperty(Property(name=Component.ACCESS_PROPERTY, value=old_access))
                 self.calendardata = str(self.calendar)
                 
-        return succeed(None)
+        returnValue(None)
 
     @inlineCallbacks
     def noUIDConflict(self, uid):
@@ -687,6 +685,7 @@
         else:
             return False
 
+    @inlineCallbacks
     def preservePrivateComments(self):
         # Check for private comments on the old resource and the new resource and re-insert
         # ones that are lost.
@@ -695,7 +694,7 @@
         # the X- property is missing.
         new_has_private_comments = False
         if config.Scheduling.CalDAV.get("EnablePrivateComments", True) and self.calendar is not None:
-            old_has_private_comments = self.destination.exists() and self.destinationcal and self.destination.hasDeadProperty(TwistedCalendarHasPrivateCommentsProperty)
+            old_has_private_comments = self.destination.exists() and self.destinationcal and (yield self.destination.hasDeadProperty(TwistedCalendarHasPrivateCommentsProperty))
             new_has_private_comments = self.calendar.hasPropertyInAnyComponent((
                 "X-CALENDARSERVER-PRIVATE-COMMENT",
                 "X-CALENDARSERVER-ATTENDEE-COMMENT",
@@ -704,19 +703,23 @@
             if old_has_private_comments and not new_has_private_comments:
                 # Transfer old comments to new calendar
                 log.debug("Private Comments properties were entirely removed by the client. Restoring existing properties.")
-                self.destination.iCalendar().addCallback(self.calendar.transferProperties,
-                                                         ("X-CALENDARSERVER-PRIVATE-COMMENT",
-                                                          "X-CALENDARSERVER-ATTENDEE-COMMENT"))
+                old_calendar = (yield self.destination.iCalendar())
+                self.calendar.transferProperties(old_calendar,
+                    (
+                        "X-CALENDARSERVER-PRIVATE-COMMENT",
+                        "X-CALENDARSERVER-ATTENDEE-COMMENT",
+                    )
+                )
                 self.calendardata = None
         
-        return new_has_private_comments
+        returnValue(new_has_private_comments)
 
     @inlineCallbacks
     def doImplicitScheduling(self):
 
         # Get any existing schedule-tag property on the resource
-        if self.destination.exists() and self.destination.hasDeadProperty(ScheduleTag):
-            self.scheduletag = self.destination.readDeadProperty(ScheduleTag)
+        if self.destination.exists() and (yield self.destination.hasDeadProperty(ScheduleTag)):
+            self.scheduletag = (yield self.destination.readDeadProperty(ScheduleTag))
             if self.scheduletag:
                 self.scheduletag = str(self.scheduletag)
         else:
@@ -795,13 +798,13 @@
     
         # Update calendar-access property value on the resource
         if self.access:
-            self.destination.writeDeadProperty(TwistedCalendarAccessProperty(self.access))
+            (yield self.destination.writeDeadProperty(TwistedCalendarAccessProperty(self.access)))
             
         # Do not remove the property if access was not specified and we are storing in a calendar.
         # This ensure that clients that do not preserve the iCalendar property do not cause access
         # restrictions to be lost.
         elif not self.destinationcal:
-            self.destination.removeDeadProperty(TwistedCalendarAccessProperty)                
+            (yield self.destination.removeDeadProperty(TwistedCalendarAccessProperty))
 
         returnValue(IResponse(response))
 
@@ -816,7 +819,7 @@
         # Finish MD5 calculation and write dead property
         md5.close()
         md5 = md5.getMD5()
-        self.destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
+        yield self.destination.writeDeadProperty(TwistedGETContentMD5.fromString(md5))
 
         returnValue(response)
 
@@ -880,6 +883,7 @@
                 ))
             return None
 
+    @inlineCallbacks
     def doDestinationIndex(self, caltoindex):
         """
         Do destination resource indexing, replacing any index previous stored.
@@ -908,10 +912,10 @@
 
         content_type = self.request.headers.getHeader("content-type")
         if not self.internal_request and content_type is not None:
-            self.destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type)))
+            yield self.destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(content_type)))
         else:
-            self.destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(MimeType("text", "calendar", params={"charset":"utf-8"}))))
-        return None
+            yield self.destination.writeDeadProperty(davxml.GETContentType.fromString(generateContentType(MimeType("text", "calendar", params={"charset":"utf-8"}))))
+        returnValue(None)
 
     def doRemoveDestinationIndex(self):
         """
@@ -962,7 +966,7 @@
             rruleChanged = self.truncateRecurrence()
 
             # Preserve private comments
-            new_has_private_comments = self.preservePrivateComments()
+            new_has_private_comments = (yield self.preservePrivateComments())
     
             # Do scheduling
             implicit_result = (yield self.doImplicitScheduling())
@@ -1021,7 +1025,7 @@
 
             # Check for scheduling object resource and write property
             if is_scheduling_resource:
-                self.destination.writeDeadProperty(TwistedSchedulingObjectResource.fromString("true"))
+                yield self.destination.writeDeadProperty(TwistedSchedulingObjectResource.fromString("true"))
 
                 # Need to figure out when to change the schedule tag:
                 #
@@ -1043,7 +1047,7 @@
 
                 if change_scheduletag or self.scheduletag is None:
                     self.scheduletag = str(uuid.uuid4())
-                self.destination.writeDeadProperty(ScheduleTag.fromString(self.scheduletag))
+                yield self.destination.writeDeadProperty(ScheduleTag.fromString(self.scheduletag))
 
                 # Add a response header
                 response.headers.setHeader("Schedule-Tag", self.scheduletag)                
@@ -1056,25 +1060,25 @@
                     else:
                         # Schedule-Tag did not change => add current ETag to list of those that can
                         # be used in a weak pre-condition test
-                        if self.destination.hasDeadProperty(TwistedScheduleMatchETags):
-                            etags = self.destination.readDeadProperty(TwistedScheduleMatchETags).children
+                        if (yield self.destination.hasDeadProperty(TwistedScheduleMatchETags)):
+                            etags = (yield self.destination.readDeadProperty(TwistedScheduleMatchETags)).children
                         else:
                             etags = ()
                     etags += (davxml.GETETag.fromString(self.destination.etag().tag),)
-                    self.destination.writeDeadProperty(TwistedScheduleMatchETags(*etags))
+                    yield self.destination.writeDeadProperty(TwistedScheduleMatchETags(*etags))
                 else:
-                    self.destination.removeDeadProperty(TwistedScheduleMatchETags)                
+                    yield self.destination.removeDeadProperty(TwistedScheduleMatchETags)                
             else:
-                self.destination.writeDeadProperty(TwistedSchedulingObjectResource.fromString("false"))                
-                self.destination.removeDeadProperty(ScheduleTag)                
-                self.destination.removeDeadProperty(TwistedScheduleMatchETags)                
+                yield self.destination.writeDeadProperty(TwistedSchedulingObjectResource.fromString("false"))                
+                yield self.destination.removeDeadProperty(ScheduleTag)                
+                yield self.destination.removeDeadProperty(TwistedScheduleMatchETags)                
 
             # Check for existence of private comments and write property
             if config.Scheduling.CalDAV.get("EnablePrivateComments", True):
                 if new_has_private_comments:
-                    self.destination.writeDeadProperty(TwistedCalendarHasPrivateCommentsProperty())
+                    yield self.destination.writeDeadProperty(TwistedCalendarHasPrivateCommentsProperty())
                 elif not self.destinationcal:
-                    self.destination.removeDeadProperty(TwistedCalendarHasPrivateCommentsProperty)                
+                    yield self.destination.removeDeadProperty(TwistedCalendarHasPrivateCommentsProperty)                
 
             # Delete the original source if needed.
             if self.deletesource:
@@ -1082,7 +1086,7 @@
     
             # Index the new resource if storing to a calendar.
             if self.destinationcal:
-                result = self.doDestinationIndex(self.calendar)
+                result = (yield self.doDestinationIndex(self.calendar))
                 if result is not None:
                     self.rollback.Rollback()
                     returnValue(result)

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/method/report_calquery.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/method/report_calquery.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/method/report_calquery.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -116,6 +116,7 @@
         @param uri: the uri for the calendar collecton resource.
         """
         
+        @inlineCallbacks
         def queryCalendarObjectResource(resource, uri, name, calendar, timezone, query_ok=False, isowner=True):
             """
             Run a query on the specified calendar.
@@ -128,7 +129,7 @@
             # Handle private events access restrictions
             if not isowner:
                 try:
-                    access = resource.readDeadProperty(TwistedCalendarAccessProperty)
+                    access = (yield resource.readDeadProperty(TwistedCalendarAccessProperty))
                 except HTTPError:
                     access = None
             else:
@@ -145,9 +146,9 @@
                 else:
                     href = davxml.HRef.fromString(uri)
             
-                return report_common.responseForHref(request, responses, href, resource, calendar, timezone, propertiesForResource, query, isowner)
+                returnValue(report_common.responseForHref(request, responses, href, resource, calendar, timezone, propertiesForResource, query, isowner))
             else:
-                return succeed(None)
+                returnValue(None)
     
         # Check whether supplied resource is a calendar or a calendar object resource
         if calresource.isPseudoCalendarCollection():

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/method/report_common.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/method/report_common.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -246,7 +246,7 @@
             # Handle private events access restrictions
             if not isowner:
                 try:
-                    access = resource.readDeadProperty(TwistedCalendarAccessProperty)
+                    access = (yield resource.readDeadProperty(TwistedCalendarAccessProperty))
                 except HTTPError:
                     access = None
             else:

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/resource.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/resource.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -230,8 +230,8 @@
         elif namespace == caldav_namespace:
             if name == "supported-calendar-component-set":
                 # CalDAV-access-09, section 5.2.3
-                if self.hasDeadProperty(qname):
-                    returnValue(self.readDeadProperty(qname))
+                if (yield self.hasDeadProperty(qname)):
+                    returnValue((yield self.readDeadProperty(qname)))
                 returnValue(self.supportedCalendarComponentSet)
             elif name == "supported-calendar-data":
                 # CalDAV-access-09, section 5.2.4
@@ -258,13 +258,13 @@
             elif name == "schedule-calendar-transp":
                 # For backwards compatibility, if the property does not exist we need to create
                 # it and default to the old free-busy-set value.
-                if self.isCalendarCollection() and not self.hasDeadProperty(property):
+                if self.isCalendarCollection() and not (yield self.hasDeadProperty(property)):
                     # For backwards compatibility we need to sync this up with the calendar-free-busy-set on the inbox
                     principal = (yield self.ownerPrincipal(request))
                     fbset = (yield principal.calendarFreeBusyURIs(request))
                     url = (yield self.canonicalURL(request))
                     opaque = url in fbset
-                    self.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Opaque() if opaque else caldavxml.Transparent()))
+                    (yield self.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Opaque()) if opaque else caldavxml.Transparent()))
 
         result = (yield super(CalDAVResource, self).readProperty(property, request))
         returnValue(result)
@@ -325,12 +325,7 @@
         result = (yield super(CalDAVResource, self).writeProperty(property, request))
         returnValue(result)
 
-    def writeDeadProperty(self, property):
-        val = super(CalDAVResource, self).writeDeadProperty(property)
 
-        return val
-
-
     ##
     # ACL
     ##
@@ -341,8 +336,8 @@
         acls = (yield super(CalDAVResource, self).accessControlList(request, *args, **kwargs))
 
         # Look for private events access classification
-        if self.hasDeadProperty(TwistedCalendarAccessProperty):
-            access = self.readDeadProperty(TwistedCalendarAccessProperty)
+        if (yield self.hasDeadProperty(TwistedCalendarAccessProperty)):
+            access = (yield self.readDeadProperty(TwistedCalendarAccessProperty))
             if access.getValue() in (Component.ACCESS_PRIVATE, Component.ACCESS_CONFIDENTIAL, Component.ACCESS_RESTRICTED,):
                 # Need to insert ACE to prevent non-owner principals from seeing this resource
                 owner = (yield self.owner(request))
@@ -461,20 +456,21 @@
         """
         return self.isSpecialCollection(caldavxml.Calendar)
 
+    @inlineCallbacks
     def isSpecialCollection(self, collectiontype):
         """
         See L{ICalDAVResource.isSpecialCollection}.
         """
-        if not self.isCollection(): return False
+        if not self.isCollection(): returnValue(False)
 
         try:
-            resourcetype = self.readDeadProperty((dav_namespace, "resourcetype"))
+            resourcetype = (yield self.readDeadProperty((dav_namespace, "resourcetype")))
         except HTTPError, e:
             assert e.response.code == responsecode.NOT_FOUND, (
                 "Unexpected response code: %s" % (e.response.code,)
             )
-            return False
-        return bool(resourcetype.childrenOfType(collectiontype))
+            returnValue(False)
+        returnValue(bool(resourcetype.childrenOfType(collectiontype)))
 
     def isPseudoCalendarCollection(self):
         """
@@ -572,21 +568,22 @@
 
             inbox = (yield request.locateResource(inboxURL))
             inbox.processFreeBusyCalendar(request.path, False)
-            inbox.processFreeBusyCalendar(destination_uri, destination.isCalendarOpaque())
+            inbox.processFreeBusyCalendar(destination_uri, (yield destination.isCalendarOpaque()))
             
             # Adjust the default calendar setting if necessary
             if defaultCalendar:
                 yield inbox.writeProperty(caldavxml.ScheduleDefaultCalendarURL(davxml.HRef(destination_path)), request)               
 
+    @inlineCallbacks
     def isCalendarOpaque(self):
         
         assert self.isCalendarCollection()
         
-        if self.hasDeadProperty((caldav_namespace, "schedule-calendar-transp")):
-            property = self.readDeadProperty((caldav_namespace, "schedule-calendar-transp"))
-            return property.children[0] == caldavxml.Opaque()
+        if (yield self.hasDeadProperty((caldav_namespace, "schedule-calendar-transp"))):
+            property = (yield self.readDeadProperty((caldav_namespace, "schedule-calendar-transp")))
+            returnValue(property.children[0] == caldavxml.Opaque())
         else:
-            return False
+            returnValue(False)
 
     @inlineCallbacks
     def isDefaultCalendar(self, request):
@@ -922,20 +919,22 @@
 
         return super(CalendarPrincipalResource, self).writeProperty(property, request)
 
+    @inlineCallbacks
     def calendarHomeURLs(self):
-        if self.hasDeadProperty((caldav_namespace, "calendar-home-set")):
-            home_set = self.readDeadProperty((caldav_namespace, "calendar-home-set"))
-            return [str(h) for h in home_set.children]
+        if (yield self.hasDeadProperty((caldav_namespace, "calendar-home-set"))):
+            home_set = (yield self.readDeadProperty((caldav_namespace, "calendar-home-set")))
+            returnValue([str(h) for h in home_set.children])
         else:
-            return ()
+            returnValue(())
 
+    @inlineCallbacks
     def calendarUserAddresses(self):
-        if self.hasDeadProperty((caldav_namespace, "calendar-user-address-set")):
-            addresses = self.readDeadProperty((caldav_namespace, "calendar-user-address-set"))
-            return [str(h) for h in addresses.children]
+        if (yield self.hasDeadProperty((caldav_namespace, "calendar-user-address-set"))):
+            addresses = (yield self.readDeadProperty((caldav_namespace, "calendar-user-address-set")))
+            returnValue([str(h) for h in addresses.children])
         else:
             # Must have a valid address of some kind so use the principal uri
-            return (self.principalURL(),)
+            returnValue((self.principalURL(),))
 
     def calendarFreeBusyURIs(self, request):
         def gotInbox(inbox):
@@ -967,32 +966,35 @@
         """
         return self.scheduleInboxURL().addCallback(request.locateResource)
 
+    @inlineCallbacks
     def scheduleInboxURL(self):
-        if self.hasDeadProperty((caldav_namespace, "schedule-inbox-URL")):
-            inbox = self.readDeadProperty((caldav_namespace, "schedule-inbox-URL"))
-            return succeed(str(inbox.children[0]))
+        if (yield self.hasDeadProperty((caldav_namespace, "schedule-inbox-URL"))):
+            inbox = (yield self.readDeadProperty((caldav_namespace, "schedule-inbox-URL")))
+            returnValue(str(inbox.children[0]))
         else:
-            return succeed(None)
+            returnValue(None)
 
+    @inlineCallbacks
     def scheduleOutboxURL(self):
         """
         @return: the schedule outbox URL for this principal.
         """
-        if self.hasDeadProperty((caldav_namespace, "schedule-outbox-URL")):
-            outbox = self.readDeadProperty((caldav_namespace, "schedule-outbox-URL"))
-            return succeed(str(outbox.children[0]))
+        if (yield self.hasDeadProperty((caldav_namespace, "schedule-outbox-URL"))):
+            outbox = (yield self.readDeadProperty((caldav_namespace, "schedule-outbox-URL")))
+            returnValue(str(outbox.children[0]))
         else:
-            return succeed(None)
+            returnValue(None)
 
+    @inlineCallbacks
     def dropboxURL(self):
         """
         @return: the drop box home collection URL for this principal.
         """
-        if self.hasDeadProperty((calendarserver_namespace, "dropbox-home-URL")):
-            inbox = self.readDeadProperty((caldav_namespace, "dropbox-home-URL"))
-            return succeed(str(inbox.children[0]))
+        if (yield self.hasDeadProperty((calendarserver_namespace, "dropbox-home-URL"))):
+            inbox = (yield self.readDeadProperty((caldav_namespace, "dropbox-home-URL")))
+            returnValue(str(inbox.children[0]))
         else:
-            return succeed(None)
+            returnValue(None)
 
     ##
     # Quota

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/static.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/static.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -107,6 +107,7 @@
             return False
         return self.fp.path == other.fp.path
 
+    @inlineCallbacks
     def checkPreconditions(self, request):
         """
         We override the base class to handle the special implicit scheduling weak ETag behavior
@@ -115,8 +116,8 @@
         
         if config.Scheduling.CalDAV.ScheduleTagCompatibility:
             
-            if self.exists() and self.hasDeadProperty(TwistedScheduleMatchETags):
-                etags = self.readDeadProperty(TwistedScheduleMatchETags).children
+            if self.exists() and (yield self.hasDeadProperty(TwistedScheduleMatchETags)):
+                etags = (yield self.readDeadProperty(TwistedScheduleMatchETags)).children
                 if len(etags) > 1:
                     # This is almost verbatim from twisted.web2.static.checkPreconditions
                     if request.method not in ("GET", "HEAD"):
@@ -144,13 +145,12 @@
                     # Check per-method preconditions
                     method = getattr(self, "preconditions_" + request.method, None)
                     if method:
-                        response = maybeDeferred(method, request)
-                        response.addCallback(lambda _: request)
-                        return response
+                        response = yield(method(request))
+                        returnValue(response)
                     else:
-                        return None
+                        returnValue(None)
 
-        return super(CalDAVFile, self).checkPreconditions(request)
+        returnValue((yield super(CalDAVFile, self).checkPreconditions(request)))
 
     def deadProperties(self, caching=True):
         if not hasattr(self, "_dead_properties"):
@@ -208,29 +208,25 @@
         parent.addCallback(_defer)
         return parent
 
+    @inlineCallbacks
     def createCalendarCollection(self):
         #
         # Create the collection once we know it is safe to do so
         #
-        def onCalendarCollection(status):
-            if status != responsecode.CREATED:
-                raise HTTPError(status)
+        status = (yield self.createSpecialCollection(davxml.ResourceType.calendar))
+        if status != responsecode.CREATED:
+            raise HTTPError(status)
 
-            # Initialize CTag on the calendar collection
-            d1 = self.updateCTag()
+        # Initialize CTag on the calendar collection
+        yield self.updateCTag()
 
-            # Calendar is initially transparent to freebusy
-            self.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Transparent()))
+        # Calendar is initially transparent to freebusy
+        yield self.writeDeadProperty(caldavxml.ScheduleCalendarTransp(caldavxml.Transparent()))
 
-            # Create the index so its ready when the first PUTs come in
-            d1.addCallback(lambda _: self.index().create())
-            d1.addCallback(lambda _: status)
-            return d1
+        # Create the index so its ready when the first PUTs come in
+        self.index().create()
+        returnValue(status)
 
-        d = self.createSpecialCollection(davxml.ResourceType.calendar)
-        d.addCallback(onCalendarCollection)
-        return d
-
     def createSpecialCollection(self, resourceType=None):
         #
         # Create the collection once we know it is safe to do so
@@ -239,8 +235,9 @@
             if status != responsecode.CREATED:
                 raise HTTPError(status)
 
-            self.writeDeadProperty(resourceType)
-            return status
+            d = self.writeDeadProperty(resourceType)
+            d.addCallback(lambda _: status)
+            return d
 
         def onError(f):
             try:
@@ -306,9 +303,10 @@
 
         raise HTTPError(ErrorResponse(responsecode.BAD_REQUEST))
 
+    @inlineCallbacks
     def iCalendarTextFiltered(self, isowner):
         try:
-            access = self.readDeadProperty(TwistedCalendarAccessProperty)
+            access = (yield self.readDeadProperty(TwistedCalendarAccessProperty))
         except HTTPError:
             access = None
 
@@ -317,9 +315,9 @@
             if not isowner:
                 # Now "filter" the resource calendar data through the CALDAV:calendar-data element and apply
                 # access restrictions to the data.
-                d = caldavxml.CalendarData().elementFromResourceWithAccessRestrictions(self, access)
-                return d.addCallback(lambda el: el.calendarData())
-        return self.iCalendarText()
+                el = (yield caldavxml.CalendarData().elementFromResourceWithAccessRestrictions(self, access))
+                returnValue(el.calendarData())
+        returnValue((yield self.iCalendarText()))
 
     def iCalendarText(self, name=None):
         if self.isPseudoCalendarCollection():
@@ -417,7 +415,7 @@
                         response = (yield original(request))
 
                         # Wipe the cache
-                        similar.deadProperties().flushCache()
+                        yield similar.deadProperties().flushCache()
 
                         returnValue(response)
 
@@ -426,13 +424,15 @@
             return similar
         return d.addCallback(_gotFile)
 
+    @inlineCallbacks
     def updateCTag(self):
         assert self.isCollection()
         try:
-            self.writeDeadProperty(customxml.GETCTag(
+            yield self.writeDeadProperty(customxml.GETCTag(
                     str(datetime.datetime.now())))
         except:
-            return fail(Failure())
+            # MOR: was: return fail(Failure())
+            raise Failure()
 
         if hasattr(self, 'clientNotifier'):
             self.clientNotifier.notify(op="update")
@@ -441,12 +441,12 @@
                       % (self,))
 
         if hasattr(self, 'cacheNotifier'):
-            return self.cacheNotifier.changed()
+            returnValue(self.cacheNotifier.changed())
         else:
             log.debug("%r does not have a cacheNotifier but the CTag changed"
                       % (self,))
 
-        return succeed(True)
+        returnValue(True)
 
     ##
     # Quota
@@ -950,10 +950,10 @@
         return self._dead_properties
 
     def etag(self):
-        return None
+        return succeed(None)
 
     def checkPreconditions(self, request):
-        return None
+        return succeed(None)
 
     ##
     # ACL
@@ -1075,10 +1075,10 @@
         return self._dead_properties
 
     def etag(self):
-        return None
+        return succeed(None)
 
     def checkPreconditions(self, request):
-        return None
+        return succeed(None)
 
     def checkPrivileges(self, request, privileges, recurse=False, principal=None, inherited_aces=None):
         return succeed(None)

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/test/test_memcacheprops.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/test/test_memcacheprops.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/test/test_memcacheprops.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -27,7 +27,7 @@
 import os
 
 from twisted.web2.http import HTTPError
-from twisted.internet.defer import succeed
+from twisted.internet.defer import succeed, inlineCallbacks, returnValue
 
 from twistedcaldav.memcacheprops import MemcachePropertyCollection
 from twistedcaldav.test.util import InMemoryPropertyStore
@@ -99,94 +99,113 @@
 
 class MemcachePropertyCollectionTestCase(TestCase):
     """
-    Test MemcacheProprtyCollection
+    Test MemcachePropertyCollection
     """
 
+    @inlineCallbacks
+    def assertRaisesDeferred(self, exception, f, *args, **kwargs):
+        try:
+            result = (yield f(*args, **kwargs))
+        except exception, inst:
+            returnValue(inst)
+        except:
+            raise self.failureException('%s raised instead of %s:\n %s'
+                                        % (sys.exc_info()[0],
+                                           exception.__name__,
+                                           failure.Failure().getTraceback()))
+        else:
+            raise self.failureException('%s not raised (%r returned)'
+                                        % (exception.__name__, result))
+
+
     def getColl(self):
         return StubCollection("calendars", ["a", "b", "c"])
 
+    @inlineCallbacks
     def test_setget(self):
 
-        child1 = self.getColl().getChild("a")
-        child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val1"))
+        child1 = (yield self.getColl().getChild("a"))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val1")))
 
-        child2 = self.getColl().getChild("a")
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+        child2 = (yield self.getColl().getChild("a"))
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop1"))).value,
             "val1")
 
-        child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val2"))
+        (yield child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val2")))
 
         # force memcache to be consulted (once per collection per request)
-        child1 = self.getColl().getChild("a")
+        child1 = (yield self.getColl().getChild("a"))
 
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop1")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop1"))).value,
             "val2")
 
+    @inlineCallbacks
     def test_merge(self):
-        child1 = self.getColl().getChild("a")
-        child2 = self.getColl().getChild("a")
-        child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val0"))
-        child1.deadProperties().set(StubProperty("ns1:", "prop2", value="val0"))
-        child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val0"))
+        child1 = (yield self.getColl().getChild("a"))
+        child2 = (yield self.getColl().getChild("a"))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val0")))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop2", value="val0")))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val0")))
 
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop1"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop3"))).value,
             "val0")
 
-        child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val1"))
-        child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val3"))
+        (yield child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val1")))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val3")))
 
         # force memcache to be consulted (once per collection per request)
-        child2 = self.getColl().getChild("a")
+        child2 = (yield self.getColl().getChild("a"))
 
         # verify properties
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop1"))).value,
             "val1")
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop3"))).value,
             "val3")
 
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop1")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop1"))).value,
             "val1")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop3"))).value,
             "val3")
 
+    @inlineCallbacks
     def test_delete(self):
-        child1 = self.getColl().getChild("a")
-        child2 = self.getColl().getChild("a")
-        child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val0"))
-        child1.deadProperties().set(StubProperty("ns1:", "prop2", value="val0"))
-        child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val0"))
+        child1 = (yield self.getColl().getChild("a"))
+        child2 = (yield self.getColl().getChild("a"))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop1", value="val0")))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop2", value="val0")))
+        (yield child1.deadProperties().set(StubProperty("ns1:", "prop3", value="val0")))
 
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop1")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop1"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop3"))).value,
             "val0")
 
-        child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val1"))
-        child1.deadProperties().delete(("ns1:", "prop1"))
-        self.assertRaises(HTTPError, child1.deadProperties().get, ("ns1:", "prop1"))
+        (yield child2.deadProperties().set(StubProperty("ns1:", "prop1", value="val1")))
+        (yield child1.deadProperties().delete(("ns1:", "prop1")))
+        self.assertRaisesDeferred(HTTPError, child1.deadProperties().get, ("ns1:", "prop1"))
 
-        self.assertFalse(child1.deadProperties().contains(("ns1:", "prop1"))) 
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertFalse((yield child1.deadProperties().contains(("ns1:", "prop1")))) 
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child1.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child1.deadProperties().get(("ns1:", "prop3"))).value,
             "val0")
 
         # force memcache to be consulted (once per collection per request)
-        child2 = self.getColl().getChild("a")
+        child2 = (yield self.getColl().getChild("a"))
 
         # verify properties
-        self.assertFalse(child2.deadProperties().contains(("ns1:", "prop1"))) 
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop2")).value,
+        self.assertFalse((yield child2.deadProperties().contains(("ns1:", "prop1")))) 
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop2"))).value,
             "val0")
-        self.assertEquals(child2.deadProperties().get(("ns1:", "prop3")).value,
+        self.assertEquals((yield child2.deadProperties().get(("ns1:", "prop3"))).value,
             "val0")

Modified: CalendarServer/branches/more-deferreds-3/twistedcaldav/test/test_resource.py
===================================================================
--- CalendarServer/branches/more-deferreds-3/twistedcaldav/test/test_resource.py	2009-08-21 21:17:01 UTC (rev 4507)
+++ CalendarServer/branches/more-deferreds-3/twistedcaldav/test/test_resource.py	2009-08-26 17:42:05 UTC (rev 4508)
@@ -31,8 +31,9 @@
         self.resource = CalDAVResource()
         self.resource._dead_properties = InMemoryPropertyStore()
 
+    @inlineCallbacks
     def test_writeDeadPropertyWritesProperty(self):
         prop = StubProperty()
-        self.resource.writeDeadProperty(prop)
-        self.assertEquals(self.resource._dead_properties.get("StubQname"),
+        yield self.resource.writeDeadProperty(prop)
+        self.assertEquals((yield self.resource._dead_properties.get("StubQname")),
                           prop)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090826/beee0d06/attachment-0001.html>


More information about the calendarserver-changes mailing list