[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