[CalendarServer-changes] [6425] CalendarServer/branches/users/glyph/more-deferreds-7
source_changes at macosforge.org
source_changes at macosforge.org
Thu Oct 14 02:48:55 PDT 2010
Revision: 6425
http://trac.macosforge.org/projects/calendarserver/changeset/6425
Author: glyph at apple.com
Date: 2010-10-14 02:48:50 -0700 (Thu, 14 Oct 2010)
Log Message:
-----------
trial txdav passing with fully async i/o (still some work to do in twistedcaldav)
Modified Paths:
--------------
CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/fileops.py
CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/resource.py
CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/storebridge.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/__init__.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/datastore/sql.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/datastore/subpostgres.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/sql.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/test/base.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/test/test_sql.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/file.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/sql.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/common.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_sql.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_util.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/util.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/sql.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/common.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_sql.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/util.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql_legacy.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/test/test_util.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/test/util.py
CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/util.py
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/fileops.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/fileops.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/fileops.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -1,111 +0,0 @@
-##
-# Copyright (c) 2005-2008 Apple Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-##
-
-"""
-Various file utilities.
-"""
-
-from twext.web2.dav.fileop import copy
-from twext.web2.dav.fileop import put
-from twext.web2.dav.xattrprops import xattrPropertyStore
-
-# This class simulates a DAVFile with enough information for use with xattrPropertyStore.
-class FakeXAttrResource(object):
-
- def __init__(self, fp):
- self.fp = fp
-
-def putWithXAttrs(stream, filepath):
- """
- Write a file to a possibly existing path and preserve any xattrs at that path.
-
- @param stream: the stream to write to the destination.
- @type stream: C{file}
- @param filepath: the destination file.
- @type filepath: L{FilePath}
- """
-
- # Preserve existings xattrs
- props = []
- if filepath.exists():
- xold = xattrPropertyStore(FakeXAttrResource(filepath))
- for item in xold.list():
- props.append((xold.get(item)))
- xold = None
-
- # First do the actual file copy
- def _gotResponse(response):
-
- # Restore original xattrs.
- if props:
- xnew = xattrPropertyStore(FakeXAttrResource(filepath))
- for prop in props:
- xnew.set(prop)
- xnew = None
-
- return response
-
- d = put(stream, filepath)
- d.addCallback(_gotResponse)
- return d
-
-def copyWithXAttrs(source_filepath, destination_filepath, destination_uri):
- """
- Copy a file from one path to another and also copy xattrs we care about.
-
- @param source_filepath: the file to copy from
- @type source_filepath: L{FilePath}
- @param destination_filepath: the file to copy to
- @type destination_filepath: L{FilePath}
- @param destination_uri: the URI of the destination resource
- @type destination_uri: C{str}
- """
-
- # First do the actual file copy
- def _gotResponse(response):
-
- # Now copy over xattrs.
- copyXAttrs(source_filepath, destination_filepath)
-
- return response
-
- d = copy(source_filepath, destination_filepath, destination_uri, "0")
- d.addCallback(_gotResponse)
- return d
-
-def copyToWithXAttrs(from_fp, to_fp):
- """
- Copy a file from one path to another and also copy xattrs we care about.
-
- @param from_fp: file being copied
- @type from_fp: L{FilePath}
- @param to_fp: file to copy to
- @type to_fp: L{FilePath}
- """
-
- # First do the actual file copy.
- from_fp.copyTo(to_fp)
-
- # Now copy over xattrs.
- copyXAttrs(from_fp, to_fp)
-
-def copyXAttrs(from_fp, to_fp):
- # Create xattr stores for each file and copy over all xattrs.
- xfrom = xattrPropertyStore(FakeXAttrResource(from_fp))
- xto = xattrPropertyStore(FakeXAttrResource(to_fp))
-
- for item in xfrom.list():
- xto.set(xfrom.get(item))
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/resource.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/resource.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -2550,8 +2550,6 @@
# Utilities
##
-def isCalendarHomeCollectionResource(resource):
- return isinstance(resource, CalendarHomeResource)
def isCalendarCollectionResource(resource):
try:
@@ -2561,6 +2559,7 @@
else:
return resource.isCalendarCollection()
+
def isPseudoCalendarCollectionResource(resource):
try:
resource = ICalDAVResource(resource)
@@ -2569,8 +2568,6 @@
else:
return resource.isPseudoCalendarCollection()
-def isAddressBookHomeCollectionResource(resource):
- return isinstance(resource, AddressBookHomeResource)
def isAddressBookCollectionResource(resource):
try:
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/storebridge.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/twistedcaldav/storebridge.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -423,7 +423,7 @@
l = []
for everyCalendar in (yield self._newStoreHome.calendars()):
for everyObject in (yield everyCalendar.calendarObjects()):
- l.append(everyObject.dropboxID())
+ l.append((yield everyObject.dropboxID()))
returnValue(l)
@@ -466,8 +466,9 @@
return davxml.ResourceType.dropbox #@UndefinedVariable
+ @inlineCallbacks
def getChild(self, name):
- attachment = self._newStoreCalendarObject.attachmentWithName(name)
+ attachment = yield self._newStoreCalendarObject.attachmentWithName(name)
if attachment is None:
result = ProtoCalendarAttachment(
self._newStoreCalendarObject,
@@ -478,7 +479,7 @@
self._newStoreCalendarObject,
attachment, principalCollections=self.principalCollections())
self.propagateTransaction(result)
- return result
+ returnValue(result)
@inlineCallbacks
@@ -523,63 +524,66 @@
return NO_CONTENT
+ @inlineCallbacks
def listChildren(self):
l = []
- for attachment in self._newStoreCalendarObject.attachments():
+ for attachment in (self._newStoreCalendarObject.attachments()):
l.append(attachment.name())
- return l
+ returnValue(l)
+ @inlineCallbacks
def accessControlList(self, *a, **kw):
"""
All principals identified as ATTENDEEs on the event for this dropbox
may read all its children. Also include proxies of ATTENDEEs.
"""
- d = super(CalendarObjectDropbox, self).accessControlList(*a, **kw)
- def moreACLs(originalACL):
- othersCanWrite = (
- self._newStoreCalendarObject.attendeesCanManageAttachments()
+ originalACL = yield super(
+ CalendarObjectDropbox, self).accessControlList(*a, **kw)
+ othersCanWrite = (
+ yield self._newStoreCalendarObject.attendeesCanManageAttachments()
+ )
+ originalACEs = list(originalACL.children)
+ cuas = (self._newStoreCalendarObject.component()).getAttendees()
+ newACEs = []
+ for calendarUserAddress in cuas:
+ principal = self.principalForCalendarUserAddress(
+ calendarUserAddress
)
- originalACEs = list(originalACL.children)
- cuas = self._newStoreCalendarObject.component().getAttendees()
- newACEs = []
- for calendarUserAddress in cuas:
- principal = self.principalForCalendarUserAddress(
- calendarUserAddress
- )
- principalURL = principal.principalURL()
- writePrivileges = [
- davxml.Privilege(davxml.Read()),
- davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
- davxml.Privilege(davxml.Write()),
- ]
- readPrivileges = [
- davxml.Privilege(davxml.Read()),
- davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
- ]
- privileges = writePrivileges if othersCanWrite else readPrivileges
- newACEs.append(davxml.ACE(
- davxml.Principal(davxml.HRef(principalURL)),
- davxml.Grant(*privileges),
- davxml.Protected(),
- TwistedACLInheritable(),
- ))
- newACEs.append(davxml.ACE(
- davxml.Principal(davxml.HRef(joinURL(principalURL, "calendar-proxy-write/"))),
- davxml.Grant(*privileges),
- davxml.Protected(),
- TwistedACLInheritable(),
- ))
- newACEs.append(davxml.ACE(
- davxml.Principal(davxml.HRef(joinURL(principalURL, "calendar-proxy-read/"))),
- davxml.Grant(*readPrivileges),
- davxml.Protected(),
- TwistedACLInheritable(),
- ))
+ principalURL = principal.principalURL()
+ writePrivileges = [
+ davxml.Privilege(davxml.Read()),
+ davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+ davxml.Privilege(davxml.Write()),
+ ]
+ readPrivileges = [
+ davxml.Privilege(davxml.Read()),
+ davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+ ]
+ if othersCanWrite:
+ privileges = writePrivileges
+ else:
+ privileges = readPrivileges
+ newACEs.append(davxml.ACE(
+ davxml.Principal(davxml.HRef(principalURL)),
+ davxml.Grant(*privileges),
+ davxml.Protected(),
+ TwistedACLInheritable(),
+ ))
+ newACEs.append(davxml.ACE(
+ davxml.Principal(davxml.HRef(joinURL(principalURL, "calendar-proxy-write/"))),
+ davxml.Grant(*privileges),
+ davxml.Protected(),
+ TwistedACLInheritable(),
+ ))
+ newACEs.append(davxml.ACE(
+ davxml.Principal(davxml.HRef(joinURL(principalURL, "calendar-proxy-read/"))),
+ davxml.Grant(*readPrivileges),
+ davxml.Protected(),
+ TwistedACLInheritable(),
+ ))
- return davxml.ACL(*tuple(newACEs + originalACEs))
- d.addCallback(moreACLs)
- return d
+ returnValue(davxml.ACL(*tuple(newACEs + originalACEs)))
@@ -591,6 +595,7 @@
self.attachmentName = attachmentName
self._newStoreObject = None
+
def isCollection(self):
return False
@@ -605,28 +610,29 @@
@requiresPermissions(fromParent=[davxml.Bind()])
+ @inlineCallbacks
def http_PUT(self, request):
# FIXME: direct test
# FIXME: transformation?
-
content_type = request.headers.getHeader("content-type")
if content_type is None:
content_type = MimeType("application", "octet-stream")
-
- t = self.calendarObject.createAttachmentWithName(
+ t = yield self.calendarObject.createAttachmentWithName(
self.attachmentName,
content_type,
)
- def done(ignored):
- self._newStoreObject = self.calendarObject.attachmentWithName(self.attachmentName)
- t.loseConnection()
- return CREATED
- return readStream(request.stream, t.write).addCallback(done)
+ yield readStream(request.stream, t.write)
+ self._newStoreObject = yield self.calendarObject.attachmentWithName(
+ self.attachmentName
+ )
+ t.loseConnection()
+ returnValue(CREATED)
http_MKCOL = None
http_MKCALENDAR = None
+
class CalendarAttachment(_NewStoreFileMetaDataHelper, _GetChildHelper):
def __init__(self, calendarObject, attachment, **kw):
@@ -890,7 +896,7 @@
yield self.downgradeFromShare(request)
# Actually delete it.
- self._newStoreParentHome.removeCalendarWithName(
+ yield self._newStoreParentHome.removeCalendarWithName(
self._newStoreCalendar.name()
)
self.__class__ = ProtoCalendarCollectionResource
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/__init__.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/__init__.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/__init__.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -1,3 +1,4 @@
+# -*- test-case-name: txdav -*-
##
# Copyright (c) 2010 Apple Inc. All rights reserved.
#
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/datastore/sql.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/datastore/sql.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from twisted.internet.defer import Deferred
"""
Logic common to SQL implementations.
@@ -68,16 +69,37 @@
@type memoAttribute: C{str}
"""
def decorate(thunk):
- spec = getargspec(thunk)
+ # cheater move to try to get the right argspec from inlineCallbacks.
+ # This could probably be more robust, but the 'cell_contents' thing
+ # probably can't (that's the only real reference to the underlying
+ # function).
+ if thunk.func_code.co_name == 'unwindGenerator':
+ specTarget = thunk.func_closure[0].cell_contents
+ else:
+ specTarget = thunk
+ spec = getargspec(specTarget)
def outer(*a, **kw):
self = a[0]
memo = getattr(self, memoAttribute)
key = _getarg(keyArgument, spec, a, kw)
if key in memo:
- return memo[key]
- result = thunk(*a, **kw)
- if result is not None:
- memo[key] = result
+ result = memo[key]
+ else:
+ result = thunk(*a, **kw)
+ if result is not None:
+ memo[key] = result
+ if isinstance(result, Deferred):
+ # clone the Deferred so that the old one keeps its result.
+ # FIXME: cancellation?
+ returnResult = Deferred()
+ def relayAndPreserve(innerResult):
+ if innerResult is None and key in memo and memo[key] is result:
+ # The result was None, call it again.
+ del memo[key]
+ returnResult.callback(innerResult)
+ return innerResult
+ result.addBoth(relayAndPreserve)
+ return returnResult
return result
return outer
return decorate
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/datastore/subpostgres.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/datastore/subpostgres.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/datastore/subpostgres.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -14,12 +14,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+import sys
"""
Run and manage PostgreSQL as a subprocess.
"""
import os
import pwd
+import thread
+
+
from hashlib import md5
from twisted.python.procutils import which
@@ -67,6 +71,10 @@
def execute(self, sql, args=()):
self.connectionWrapper.state = 'executing %r' % (sql,)
+# sys.stdout.write(
+# "Really executing SQL %r in thread %r\n" %
+# ((sql % tuple(args)), thread.get_ident())
+# )
self.realCursor.execute(sql, args)
@@ -75,7 +83,12 @@
def fetchall(self):
- return self.realCursor.fetchall()
+ results = self.realCursor.fetchall()
+# sys.stdout.write(
+# "Really fetching results %r thread %r\n" %
+# (results, thread.get_ident())
+# )
+ return results
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/sql.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/sql.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/sql.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from twisted.internet.defer import inlineCallbacks, returnValue
"""
PostgreSQL data store.
@@ -30,31 +31,35 @@
class PropertyStore(AbstractPropertyStore):
- def __init__(self, defaultuser, txn, resourceID):
+ def __init__(self, *a, **kw):
+ raise NotImplementedError(
+ "do not construct directly, call PropertyStore.load()"
+ )
+
+
+ @classmethod
+ @inlineCallbacks
+ def load(cls, defaultuser, txn, resourceID):
+ self = cls.__new__(cls)
super(PropertyStore, self).__init__(defaultuser)
self._txn = txn
self._resourceID = resourceID
+ self._cached = {}
+ rows = yield self._txn.execSQL(
+ """
+ select NAME, VIEWER_UID, VALUE from RESOURCE_PROPERTY
+ where RESOURCE_ID = %s
+ """,
+ [self._resourceID]
+ )
+ for name, uid, value in rows:
+ self._cached[(name, uid)] = value
+ returnValue(self)
- @property
- def _cached(self):
-
- if not hasattr(self, "_cached_properties"):
- self._cached_properties = {}
- rows = self._txn.execSQL(
- """
- select NAME, VIEWER_UID, VALUE from RESOURCE_PROPERTY
- where RESOURCE_ID = %s
- """,
- [self._resourceID]
- )
- for name, uid, value in rows:
- self._cached_properties[(name, uid)] = value
-
- return self._cached_properties
def _getitem_uid(self, key, uid):
validKey(key)
-
+
try:
value = self._cached[(key.toString(), uid)]
except KeyError:
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/test/base.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/test/base.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/test/base.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -27,6 +27,7 @@
from zope.interface.verify import verifyObject, BrokenMethodImplementation
+from twisted.internet.defer import inlineCallbacks
from twisted.trial import unittest
from twext.web2.dav import davxml
@@ -49,6 +50,8 @@
except BrokenMethodImplementation, e:
self.fail(e)
+
+ @inlineCallbacks
def test_set_get_contains(self):
name = propertyName("test")
@@ -56,7 +59,7 @@
# Test with commit after change
self.propertyStore[name] = value
- self._changed(self.propertyStore)
+ yield self._changed(self.propertyStore)
self.assertEquals(self.propertyStore.get(name, None), value)
self.failUnless(name in self.propertyStore)
@@ -66,6 +69,8 @@
self.assertEquals(self.propertyStore.get(name, None), value)
self.failUnless(name in self.propertyStore)
+
+ @inlineCallbacks
def test_delete_get_contains(self):
# Test with commit after change
@@ -73,10 +78,10 @@
value = propertyValue("Hello, World!")
self.propertyStore[name] = value
- self._changed(self.propertyStore)
+ yield self._changed(self.propertyStore)
del self.propertyStore[name]
- self._changed(self.propertyStore)
+ yield self._changed(self.propertyStore)
self.assertEquals(self.propertyStore.get(name, None), None)
self.failIf(name in self.propertyStore)
@@ -86,13 +91,15 @@
value = propertyValue("Hello, Universe!")
self.propertyStore[name] = value
- self._changed(self.propertyStore)
+ yield self._changed(self.propertyStore)
del self.propertyStore[name]
self.assertEquals(self.propertyStore.get(name, None), None)
self.failIf(name in self.propertyStore)
+
+ @inlineCallbacks
def test_peruser(self):
name = propertyName("test")
@@ -100,35 +107,37 @@
value2 = propertyValue("Hello, World2!")
self.propertyStore1[name] = value1
- self._changed(self.propertyStore1)
+ yield self._changed(self.propertyStore1)
self.assertEquals(self.propertyStore1.get(name, None), value1)
self.assertEquals(self.propertyStore2.get(name, None), None)
self.failUnless(name in self.propertyStore1)
self.failIf(name in self.propertyStore2)
-
+
self.propertyStore2[name] = value2
- self._changed(self.propertyStore2)
+ yield self._changed(self.propertyStore2)
self.assertEquals(self.propertyStore1.get(name, None), value1)
self.assertEquals(self.propertyStore2.get(name, None), value2)
self.failUnless(name in self.propertyStore1)
self.failUnless(name in self.propertyStore2)
-
+
del self.propertyStore2[name]
- self._changed(self.propertyStore2)
+ yield self._changed(self.propertyStore2)
self.assertEquals(self.propertyStore1.get(name, None), value1)
self.assertEquals(self.propertyStore2.get(name, None), None)
self.failUnless(name in self.propertyStore1)
self.failIf(name in self.propertyStore2)
-
+
del self.propertyStore1[name]
- self._changed(self.propertyStore1)
+ yield self._changed(self.propertyStore1)
self.assertEquals(self.propertyStore1.get(name, None), None)
self.assertEquals(self.propertyStore2.get(name, None), None)
self.failIf(name in self.propertyStore1)
self.failIf(name in self.propertyStore2)
-
- def test_peruser_shadow(self):
+
+ @inlineCallbacks
+ def test_peruserShadow(self):
+
name = propertyName("shadow")
self.propertyStore1.setSpecialProperties((name,), ())
@@ -138,34 +147,35 @@
value2 = propertyValue("Hello, World2!")
self.propertyStore1[name] = value1
- self._changed(self.propertyStore1)
+ yield self._changed(self.propertyStore1)
self.assertEquals(self.propertyStore1.get(name, None), value1)
self.assertEquals(self.propertyStore2.get(name, None), value1)
self.failUnless(name in self.propertyStore1)
self.failUnless(name in self.propertyStore2)
-
+
self.propertyStore2[name] = value2
- self._changed(self.propertyStore2)
+ yield self._changed(self.propertyStore2)
self.assertEquals(self.propertyStore1.get(name, None), value1)
self.assertEquals(self.propertyStore2.get(name, None), value2)
self.failUnless(name in self.propertyStore1)
self.failUnless(name in self.propertyStore2)
-
+
del self.propertyStore2[name]
- self._changed(self.propertyStore2)
+ yield self._changed(self.propertyStore2)
self.assertEquals(self.propertyStore1.get(name, None), value1)
self.assertEquals(self.propertyStore2.get(name, None), value1)
self.failUnless(name in self.propertyStore1)
self.failUnless(name in self.propertyStore2)
-
+
del self.propertyStore1[name]
- self._changed(self.propertyStore1)
+ yield self._changed(self.propertyStore1)
self.assertEquals(self.propertyStore1.get(name, None), None)
self.assertEquals(self.propertyStore2.get(name, None), None)
self.failIf(name in self.propertyStore1)
self.failIf(name in self.propertyStore2)
+ @inlineCallbacks
def test_peruser_global(self):
name = propertyName("global")
@@ -177,32 +187,32 @@
value2 = propertyValue("Hello, World2!")
self.propertyStore1[name] = value1
- self._changed(self.propertyStore1)
+ yield self._changed(self.propertyStore1)
self.assertEquals(self.propertyStore1.get(name, None), value1)
self.assertEquals(self.propertyStore2.get(name, None), value1)
self.failUnless(name in self.propertyStore1)
self.failUnless(name in self.propertyStore2)
-
+
self.propertyStore2[name] = value2
- self._changed(self.propertyStore2)
+ yield self._changed(self.propertyStore2)
self.assertEquals(self.propertyStore1.get(name, None), value2)
self.assertEquals(self.propertyStore2.get(name, None), value2)
self.failUnless(name in self.propertyStore1)
self.failUnless(name in self.propertyStore2)
-
+
del self.propertyStore2[name]
- self._changed(self.propertyStore2)
+ yield self._changed(self.propertyStore2)
self.assertEquals(self.propertyStore1.get(name, None), None)
self.assertEquals(self.propertyStore2.get(name, None), None)
self.failIf(name in self.propertyStore1)
self.failIf(name in self.propertyStore2)
-
+
def test_iteration(self):
value = propertyValue("Hello, World!")
- names = set(propertyName(str(i)) for i in (1,2,3,4))
+ names = set(propertyName(str(i)) for i in (1, 2, 3, 4))
for name in names:
self.propertyStore[name] = value
@@ -210,6 +220,7 @@
self.assertEquals(set(self.propertyStore.keys()), names)
self.assertEquals(len(self.propertyStore), len(names))
+
def test_delete_none(self):
def doDelete():
@@ -217,9 +228,9 @@
self.assertRaises(KeyError, doDelete)
+
def test_keyInPropertyName(self):
-
def doGet():
self.propertyStore["xyzzy"]
@@ -237,6 +248,8 @@
self.assertRaises(TypeError, doDelete)
self.assertRaises(TypeError, doContains)
+
+ @inlineCallbacks
def test_flush(self):
name = propertyName("test")
@@ -247,8 +260,8 @@
#
self.propertyStore[name] = value
- self._changed(self.propertyStore)
- self._abort(self.propertyStore)
+ yield self._changed(self.propertyStore)
+ yield self._abort(self.propertyStore)
self.assertEquals(self.propertyStore.get(name, None), value)
self.assertEquals(len(self.propertyStore), 1)
@@ -258,23 +271,27 @@
#
del self.propertyStore[name]
- self._changed(self.propertyStore)
- self._abort(self.propertyStore)
+ yield self._changed(self.propertyStore)
+ yield self._abort(self.propertyStore)
self.assertEquals(self.propertyStore.get(name, None), None)
self.assertEquals(len(self.propertyStore), 0)
+
+ @inlineCallbacks
def test_abort(self):
name = propertyName("test")
value = propertyValue("Hello, World!")
self.propertyStore[name] = value
- self._abort(self.propertyStore)
+ yield self._abort(self.propertyStore)
self.assertEquals(self.propertyStore.get(name, None), None)
self.assertEquals(len(self.propertyStore), 0)
+
+ @inlineCallbacks
def test_peruser_keys(self):
name = propertyName("shadow")
@@ -285,7 +302,7 @@
value1 = propertyValue("Hello, World1!")
self.propertyStore1[name] = value1
- self._changed(self.propertyStore1)
+ yield self._changed(self.propertyStore1)
self.failUnless(name in self.propertyStore2.keys())
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/test/test_sql.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/base/propertystore/test/test_sql.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -42,10 +42,10 @@
self.notifierFactory = StubNotifierFactory()
self.store = yield buildStore(self, self.notifierFactory)
self._txn = self.store.newTransaction()
- self.propertyStore = self.propertyStore1 = PropertyStore(
+ self.propertyStore = self.propertyStore1 = yield PropertyStore.load(
"user01", self._txn, 1
)
- self.propertyStore2 = PropertyStore("user01", self._txn, 1)
+ self.propertyStore2 = yield PropertyStore.load("user01", self._txn, 1)
self.propertyStore2._setPerUserUID("user02")
@@ -60,42 +60,44 @@
returnValue(result)
+ @inlineCallbacks
def _changed(self, store):
if hasattr(self, "_txn"):
- self._txn.commit()
+ yield self._txn.commit()
delattr(self, "_txn")
self._txn = self.store.newTransaction()
store = self.propertyStore1
- self.propertyStore = self.propertyStore1 = PropertyStore(
+ self.propertyStore = self.propertyStore1 = yield PropertyStore.load(
"user01", self._txn, 1
)
self.propertyStore1._shadowableKeys = store._shadowableKeys
self.propertyStore1._globalKeys = store._globalKeys
store = self.propertyStore2
- self.propertyStore2 = PropertyStore("user01", self._txn, 1)
+ self.propertyStore2 = yield PropertyStore.load("user01", self._txn, 1)
self.propertyStore2._setPerUserUID("user02")
self.propertyStore2._shadowableKeys = store._shadowableKeys
self.propertyStore2._globalKeys = store._globalKeys
+ @inlineCallbacks
def _abort(self, store):
if hasattr(self, "_txn"):
- self._txn.abort()
+ yield self._txn.abort()
delattr(self, "_txn")
self._txn = self.store.newTransaction()
store = self.propertyStore1
- self.propertyStore = self.propertyStore1 = PropertyStore(
+ self.propertyStore = self.propertyStore1 = yield PropertyStore.load(
"user01", self._txn, 1
)
self.propertyStore1._shadowableKeys = store._shadowableKeys
self.propertyStore1._globalKeys = store._globalKeys
store = self.propertyStore2
- self.propertyStore2 = PropertyStore("user01", self._txn, 1)
+ self.propertyStore2 = yield PropertyStore.load("user01", self._txn, 1)
self.propertyStore2._setPerUserUID("user02")
self.propertyStore2._shadowableKeys = store._shadowableKeys
self.propertyStore2._globalKeys = store._globalKeys
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/file.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/file.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/file.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from twisted.internet.defer import inlineCallbacks, returnValue
"""
File calendar store.
@@ -101,6 +102,7 @@
for child in self.listCalendars():
yield self.calendarWithName(child)
+
def listCalendars(self):
"""
Return a generator of the child resource names.
@@ -111,14 +113,16 @@
yield name
+
+ @inlineCallbacks
def calendarObjectWithDropboxID(self, dropboxID):
"""
Implement lookup with brute-force scanning.
"""
for calendar in self.calendars():
for calendarObject in calendar.calendarObjects():
- if dropboxID == calendarObject.dropboxID():
- return calendarObject
+ if dropboxID == (yield calendarObject.dropboxID()):
+ returnValue(calendarObject)
@property
@@ -333,26 +337,30 @@
return self.component().getOrganizer()
+ @inlineCallbacks
def createAttachmentWithName(self, name, contentType):
"""
Implement L{ICalendarObject.removeAttachmentWithName}.
"""
# Make a (FIXME: temp, remember rollbacks) file in dropbox-land
- attachment = Attachment(self, name)
+ dropboxPath = yield self._dropboxPath()
+ attachment = Attachment(self, name, dropboxPath)
self._attachments[name] = attachment
- return attachment.store(contentType)
+ returnValue(attachment.store(contentType))
+ @inlineCallbacks
def removeAttachmentWithName(self, name):
"""
Implement L{ICalendarObject.removeAttachmentWithName}.
"""
# FIXME: rollback, tests for rollback
- self._dropboxPath().child(name).remove()
+ (yield self._dropboxPath()).child(name).remove()
if name in self._attachments:
del self._attachments[name]
+ @inlineCallbacks
def attachmentWithName(self, name):
# Attachments can be local or remote, but right now we only care about
# local. So we're going to base this on the listing of files in the
@@ -360,37 +368,46 @@
# 'attach' properties.
if name in self._attachments:
- return self._attachments[name]
+ returnValue(self._attachments[name])
# FIXME: cache consistently (put it in self._attachments)
- if self._dropboxPath().child(name).exists():
- return Attachment(self, name)
+ dbp = yield self._dropboxPath()
+ if dbp.child(name).exists():
+ returnValue(Attachment(self, name, dbp))
else:
# FIXME: test for non-existent attachment.
- return None
+ returnValue(None)
+ @inlineCallbacks
def attendeesCanManageAttachments(self):
- return self.component().hasPropertyInAnyComponent("X-APPLE-DROPBOX")
+ returnValue((yield self.component()).hasPropertyInAnyComponent("X-APPLE-DROPBOX"))
def dropboxID(self):
+ # NB: Deferred
return dropboxIDFromCalendarObject(self)
+ @inlineCallbacks
def _dropboxPath(self):
dropboxPath = self._parentCollection._home._path.child(
"dropbox"
- ).child(self.dropboxID())
+ ).child((yield self.dropboxID()))
if not dropboxPath.isdir():
dropboxPath.makedirs()
- return dropboxPath
+ returnValue(dropboxPath)
+ @inlineCallbacks
def attachments(self):
# See comment on attachmentWithName.
- return [Attachment(self, name)
- for name in self._dropboxPath().listdir()]
+ dropboxPath = (yield self._dropboxPath())
+ returnValue(
+ [Attachment(self, name, dropboxPath)
+ for name in dropboxPath.listdir()]
+ )
+
def initPropertyStore(self, props):
# Setup peruser special properties
props.setSpecialProperties(
@@ -409,6 +426,7 @@
)
+
contentTypeKey = PropertyName.fromElement(GETContentType)
md5key = PropertyName.fromElement(TwistedGETContentMD5)
@@ -458,9 +476,10 @@
implements(IAttachment)
- def __init__(self, calendarObject, name):
+ def __init__(self, calendarObject, name, dropboxPath):
self._calendarObject = calendarObject
self._name = name
+ self._dropboxPath = dropboxPath
def name(self):
@@ -475,6 +494,7 @@
def store(self, contentType):
return AttachmentStorageTransport(self, contentType)
+
def retrieve(self, protocol):
# FIXME: makeConnection
# FIXME: actually stream
@@ -483,10 +503,10 @@
# FIXME: ConnectionDone, not NotImplementedError
protocol.connectionLost(Failure(NotImplementedError()))
+
@property
def _path(self):
- dropboxPath = self._calendarObject._dropboxPath()
- return dropboxPath.child(self.name())
+ return self._dropboxPath.child(self.name())
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/index_file.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -42,7 +42,7 @@
from vobject.icalendar import utc
-from twisted.internet.defer import maybeDeferred, succeed
+from twisted.internet.defer import maybeDeferred, succeed, inlineCallbacks
from twext.python.log import Logger, LoggingMixIn
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/sql.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/sql.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -14,7 +14,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
-from twisted.internet.defer import inlineCallbacks, returnValue
__all__ = [
"CalendarHome",
@@ -26,6 +25,7 @@
from twext.web2.dav.element.rfc2518 import ResourceType
from twext.web2.http_headers import MimeType, generateContentType
+from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.error import ConnectionLost
from twisted.internet.interfaces import ITransport
from twisted.python import hashlib
@@ -89,13 +89,14 @@
returnValue(calendarObject)
+ @inlineCallbacks
def createdHome(self):
- self.createCalendarWithName("calendar")
- defaultCal = self.calendarWithName("calendar")
+ yield self.createCalendarWithName("calendar")
+ defaultCal = yield self.calendarWithName("calendar")
props = defaultCal.properties()
props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(
Opaque())
- self.createCalendarWithName("inbox")
+ yield self.createCalendarWithName("inbox")
@@ -218,11 +219,8 @@
class CalendarObject(CommonObjectResource):
implements(ICalendarObject)
- def __init__(self, name, calendar, resid):
- super(CalendarObject, self).__init__(name, calendar, resid)
+ _objectTable = CALENDAR_OBJECT_TABLE
- self._objectTable = CALENDAR_OBJECT_TABLE
-
@property
def _calendar(self):
return self._parentCollection
@@ -241,6 +239,8 @@
self._calendar.notifyChanged()
+
+ @inlineCallbacks
def updateDatabase(self, component, expand_until=None, reCreate=False, inserting=False):
"""
Update the database tables for the new data being written.
@@ -284,7 +284,7 @@
# CALENDAR_OBJECT table update
if inserting:
- self._resourceID = self._txn.execSQL(
+ self._resourceID = (yield self._txn.execSQL(
"""
insert into CALENDAR_OBJECT
(CALENDAR_RESOURCE_ID, RESOURCE_NAME, ICALENDAR_TEXT, ICALENDAR_UID, ICALENDAR_TYPE, ATTACHMENTS_MODE, ORGANIZER, RECURRANCE_MAX)
@@ -304,7 +304,7 @@
organizer,
normalizeForIndex(instances.limit) if instances.limit else None,
]
- )[0][0]
+ ))[0][0]
else:
self._txn.execSQL(
"""
@@ -337,6 +337,7 @@
self._resourceID,
],
)
+ self._uid = component.resourceUID()
# CALENDAR_OBJECT table update
@@ -346,7 +347,7 @@
end = instance.end.replace(tzinfo=utc)
float = instance.start.tzinfo is None
transp = instance.component.propertyValue("TRANSP") == "TRANSPARENT"
- instanceid = self._txn.execSQL(
+ instanceid = (yield self._txn.execSQL(
"""
insert into TIME_RANGE
(CALENDAR_RESOURCE_ID, CALENDAR_OBJECT_RESOURCE_ID, FLOATING, START_DATE, END_DATE, FBTYPE, TRANSPARENT)
@@ -364,7 +365,7 @@
icalfbtype_to_indexfbtype.get(instance.component.getFBType(), icalfbtype_to_indexfbtype["FREE"]),
transp,
],
- )[0][0]
+ ))[0][0]
peruserdata = component.perUserTransparency(instance.rid)
for useruid, transp in peruserdata:
self._txn.execSQL(
@@ -387,7 +388,7 @@
start = datetime.datetime(2100, 1, 1, 0, 0, 0, tzinfo=utc)
end = datetime.datetime(2100, 1, 1, 1, 0, 0, tzinfo=utc)
float = False
- instanceid = self._txn.execSQL(
+ instanceid = (yield self._txn.execSQL(
"""
insert into TIME_RANGE
(CALENDAR_RESOURCE_ID, CALENDAR_OBJECT_RESOURCE_ID, FLOATING, START_DATE, END_DATE, FBTYPE, TRANSPARENT)
@@ -405,7 +406,7 @@
icalfbtype_to_indexfbtype["UNKNOWN"],
True,
],
- )[0][0]
+ ))[0][0]
peruserdata = component.perUserTransparency(None)
for useruid, transp in peruserdata:
self._txn.execSQL(
@@ -422,34 +423,21 @@
],
)
+
+ @inlineCallbacks
def component(self):
- return VComponent.fromString(self.iCalendarText())
+ returnValue(VComponent.fromString((yield self.iCalendarText())))
- def text(self):
- if self._objectText is None:
- text = self._txn.execSQL(
- "select ICALENDAR_TEXT from CALENDAR_OBJECT where "
- "RESOURCE_ID = %s", [self._resourceID]
- )[0][0]
- self._objectText = text
- return text
- else:
- return self._objectText
- iCalendarText = text
+ iCalendarText = CommonObjectResource.text
- def uid(self):
- return self.component().resourceUID()
- def name(self):
- return self._name
+ @inlineCallbacks
+ def organizer(self):
+ returnValue((yield self.component()).getOrganizer())
- def componentType(self):
- return self.component().mainType()
- def organizer(self):
- return self.component().getOrganizer()
-
+ @inlineCallbacks
def createAttachmentWithName(self, name, contentType):
try:
@@ -458,56 +446,66 @@
pass
attachment = Attachment(self, name)
- self._txn.execSQL("""
+ yield self._txn.execSQL(
+ """
insert into ATTACHMENT (CALENDAR_OBJECT_RESOURCE_ID, CONTENT_TYPE,
- SIZE, MD5, PATH)
- values (%s, %s, %s, %s, %s)
+ SIZE, MD5, PATH) values (%s, %s, %s, %s, %s)
""",
[
- self._resourceID,
- generateContentType(contentType),
- 0,
- "",
+ self._resourceID, generateContentType(contentType), 0, "",
name,
]
)
- return attachment.store(contentType)
+ returnValue(attachment.store(contentType))
+
def removeAttachmentWithName(self, name):
attachment = Attachment(self, name)
self._txn.postCommit(attachment._path.remove)
- self._txn.execSQL("""
- delete from ATTACHMENT where CALENDAR_OBJECT_RESOURCE_ID = %s AND
- PATH = %s
- """, [self._resourceID, name])
+ self._txn.execSQL(
+ """
+ delete from ATTACHMENT where CALENDAR_OBJECT_RESOURCE_ID = %s AND
+ PATH = %s
+ """, [self._resourceID, name]
+ )
+
+ @inlineCallbacks
def attachmentWithName(self, name):
attachment = Attachment(self, name)
- if attachment._populate():
- return attachment
+ if (yield attachment._populate()):
+ returnValue(attachment)
else:
- return None
+ returnValue(None)
+
def attendeesCanManageAttachments(self):
return self.component().hasPropertyInAnyComponent("X-APPLE-DROPBOX")
- def dropboxID(self):
- return dropboxIDFromCalendarObject(self)
+ dropboxID = dropboxIDFromCalendarObject
+
+
def _attachmentPathRoot(self):
attachmentRoot = self._txn._store.attachmentsPath
# Use directory hashing scheme based on owner user id
homeName = self._calendar.ownerHome().name()
return attachmentRoot.child(homeName[0:2]).child(homeName[2:4]).child(homeName).child(self.uid())
-
+
+
+ @inlineCallbacks
def attachments(self):
- rows = self._txn.execSQL("""
- select PATH from ATTACHMENT where CALENDAR_OBJECT_RESOURCE_ID = %s
- """, [self._resourceID])
+ rows = yield self._txn.execSQL(
+ """
+ select PATH from ATTACHMENT where CALENDAR_OBJECT_RESOURCE_ID = %s
+ """, [self._resourceID])
+ result = []
for row in rows:
- yield self.attachmentWithName(row[0])
+ result.append((yield self.attachmentWithName(row[0])))
+ returnValue(result)
+
def initPropertyStore(self, props):
# Setup peruser special properties
props.setSpecialProperties(
@@ -557,8 +555,10 @@
self.attachment._path.setContent(self.buf)
contentTypeString = generateContentType(self.contentType)
self._txn.execSQL(
- "update ATTACHMENT set CONTENT_TYPE = %s, SIZE = %s, MD5 = %s, MODIFIED = timezone('UTC', CURRENT_TIMESTAMP) "
- "WHERE PATH = %s",
+ """
+ update ATTACHMENT set CONTENT_TYPE = %s, SIZE = %s, MD5 = %s,
+ MODIFIED = timezone('UTC', CURRENT_TIMESTAMP) WHERE PATH = %s
+ """,
[contentTypeString, len(self.buf), self.hash.hexdigest(), self.attachment.name()]
)
@@ -576,24 +576,25 @@
return self._calendarObject._txn
+ @inlineCallbacks
def _populate(self):
"""
Execute necessary SQL queries to retrieve attributes.
@return: C{True} if this attachment exists, C{False} otherwise.
"""
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"""
select CONTENT_TYPE, SIZE, MD5, CREATED, MODIFIED from ATTACHMENT where PATH = %s
""", [self._name])
if not rows:
- return False
+ returnValue(False)
self._contentType = MimeType.fromString(rows[0][0])
self._size = rows[0][1]
self._md5 = rows[0][2]
self._created = datetimeMktime(datetime.datetime.strptime(rows[0][3], "%Y-%m-%d %H:%M:%S.%f"))
self._modified = datetimeMktime(datetime.datetime.strptime(rows[0][4], "%Y-%m-%d %H:%M:%S.%f"))
- return True
+ returnValue(True)
def name(self):
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/common.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/common.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -19,7 +19,8 @@
Tests for common calendar store API functions.
"""
-from twisted.internet.defer import Deferred, inlineCallbacks, returnValue
+from twisted.internet.defer import Deferred, inlineCallbacks, returnValue,\
+ maybeDeferred
from twisted.internet.protocol import Protocol
from txdav.idav import IPropertyStore, IDataStore, AlreadyFinishedError
@@ -253,60 +254,65 @@
)
+ @inlineCallbacks
def notificationUnderTest(self):
txn = self.transactionUnderTest()
- notifications = txn.notificationsWithUID("home1")
+ notifications = yield txn.notificationsWithUID("home1")
inviteNotification = InviteNotification()
- notifications.writeNotificationObject("abc", inviteNotification,
+ yield notifications.writeNotificationObject("abc", inviteNotification,
inviteNotification.toxml())
- notificationObject = notifications.notificationObjectWithUID("abc")
- return notificationObject
+ notificationObject = yield notifications.notificationObjectWithUID("abc")
+ returnValue(notificationObject)
+ @inlineCallbacks
def test_notificationObjectProvides(self):
"""
The objects retrieved from the notification home (the object returned
from L{notificationsWithUID}) provide L{INotificationObject}.
"""
- notificationObject = self.notificationUnderTest()
+ notificationObject = yield self.notificationUnderTest()
self.assertProvides(INotificationObject, notificationObject)
+ @inlineCallbacks
def test_replaceNotification(self):
"""
L{INotificationCollection.writeNotificationObject} will silently
overwrite the notification object.
"""
- notifications = self.transactionUnderTest().notificationsWithUID(
+ notifications = yield self.transactionUnderTest().notificationsWithUID(
"home1"
)
inviteNotification = InviteNotification()
- notifications.writeNotificationObject("abc", inviteNotification,
+ yield notifications.writeNotificationObject("abc", inviteNotification,
inviteNotification.toxml())
inviteNotification2 = InviteNotification(InviteSummary("a summary"))
- notifications.writeNotificationObject(
+ yield notifications.writeNotificationObject(
"abc", inviteNotification, inviteNotification2.toxml())
- abc = notifications.notificationObjectWithUID("abc")
- self.assertEquals(abc.xmldata(), inviteNotification2.toxml())
+ abc = yield notifications.notificationObjectWithUID("abc")
+ self.assertEquals((yield abc.xmldata()), inviteNotification2.toxml())
+ @inlineCallbacks
def test_notificationObjectModified(self):
"""
The objects retrieved from the notification home have a C{modified}
method which returns the timestamp of their last modification.
"""
- notification = self.notificationUnderTest()
- self.assertIsInstance(notification.modified(), int)
+ notification = yield self.notificationUnderTest()
+ self.assertIsInstance((yield notification.modified()), int)
+ @inlineCallbacks
def test_notificationObjectParent(self):
"""
L{INotificationObject.notificationCollection} returns the
L{INotificationCollection} that the object was retrieved from.
"""
txn = self.transactionUnderTest()
- collection = txn.notificationsWithUID("home1")
- notification = self.notificationUnderTest()
+ collection = yield txn.notificationsWithUID("home1")
+ notification = yield self.notificationUnderTest()
self.assertIdentical(collection, notification.notificationCollection())
@@ -364,7 +370,7 @@
"""
home = yield self.homeUnderTest()
calendar = yield home.calendarWithName("calendar_1")
- calendar.rename("some_other_name")
+ yield calendar.rename("some_other_name")
@inlineCallbacks
def positiveAssertions():
self.assertEquals(calendar.name(), "some_other_name")
@@ -402,7 +408,7 @@
home = yield self.homeUnderTest()
name = "new"
self.assertIdentical((yield home.calendarWithName(name)), None)
- home.createCalendarWithName(name)
+ yield home.createCalendarWithName(name)
self.assertNotIdentical((yield home.calendarWithName(name)), None)
@inlineCallbacks
def checkProperties():
@@ -443,9 +449,9 @@
"""
home = yield self.homeUnderTest()
for name in home1_calendarNames:
- self.assertRaises(
- HomeChildNameAlreadyExistsError,
- home.createCalendarWithName, name
+ yield self.failUnlessFailure(
+ maybeDeferred(home.createCalendarWithName, name),
+ HomeChildNameAlreadyExistsError
)
@@ -460,7 +466,7 @@
# FIXME: test transactions
for name in home1_calendarNames:
self.assertNotIdentical((yield home.calendarWithName(name)), None)
- home.removeCalendarWithName(name)
+ yield home.removeCalendarWithName(name)
self.assertEquals((yield home.calendarWithName(name)), None)
yield self.commit()
@@ -485,8 +491,10 @@
Attempt to remove an non-existing calendar should raise.
"""
home = yield self.homeUnderTest()
- self.assertRaises(NoSuchHomeChildError,
- home.removeCalendarWithName, "xyzzy")
+ yield self.failUnlessFailure(
+ maybeDeferred(home.removeCalendarWithName, "xyzzy"),
+ NoSuchHomeChildError
+ )
@inlineCallbacks
@@ -570,7 +578,7 @@
uid = (u'uid' + name.rstrip(".ics"))
self.assertNotIdentical((yield calendar.calendarObjectWithUID(uid)),
None)
- calendar.removeCalendarObjectWithUID(uid)
+ yield calendar.removeCalendarObjectWithUID(uid)
self.assertEquals(
(yield calendar.calendarObjectWithUID(uid)),
None
@@ -604,7 +612,7 @@
self.assertNotIdentical(
(yield calendar.calendarObjectWithName(name)), None
)
- calendar.removeCalendarObjectWithName(name)
+ yield calendar.removeCalendarObjectWithName(name)
self.assertIdentical(
(yield calendar.calendarObjectWithName(name)), None
)
@@ -616,9 +624,9 @@
Attempt to remove an non-existing calendar object should raise.
"""
calendar = yield self.calendarUnderTest()
- self.assertRaises(
- NoSuchObjectResourceError,
- calendar.removeCalendarObjectWithName, "xyzzy"
+ yield self.failUnlessFailure(
+ maybeDeferred(calendar.removeCalendarObjectWithName, "xyzzy"),
+ NoSuchObjectResourceError
)
@@ -647,7 +655,7 @@
L{ICalendarObject.component} returns a L{VComponent} describing the
calendar data underlying that calendar object.
"""
- component = (yield self.calendarObjectUnderTest()).component()
+ component = yield (yield self.calendarObjectUnderTest()).component()
self.failUnless(
isinstance(component, VComponent),
@@ -665,7 +673,7 @@
L{ICalendarObject.iCalendarText} returns a C{str} describing the same
data provided by L{ICalendarObject.component}.
"""
- text = (yield self.calendarObjectUnderTest()).iCalendarText()
+ text = yield (yield self.calendarObjectUnderTest()).iCalendarText()
self.assertIsInstance(text, str)
self.failUnless(text.startswith("BEGIN:VCALENDAR\r\n"))
self.assertIn("\r\nUID:uid1\r\n", text)
@@ -738,7 +746,7 @@
home = yield self.homeUnderTest()
allCalendars = yield home.calendars()
before = set(x.name() for x in allCalendars)
- home.createCalendarWithName("new-name")
+ yield home.createCalendarWithName("new-name")
allCalendars = yield home.calendars()
after = set(x.name() for x in allCalendars)
self.assertEquals(before | set(['new-name']), after)
@@ -752,12 +760,14 @@
"""
calendar1 = yield self.calendarUnderTest()
name = "4.ics"
- self.assertIdentical((yield calendar1.calendarObjectWithName(name)), None)
+ self.assertIdentical(
+ (yield calendar1.calendarObjectWithName(name)), None
+ )
component = VComponent.fromString(event4_text)
- calendar1.createCalendarObjectWithName(name, component)
+ yield calendar1.createCalendarObjectWithName(name, component)
calendarObject = yield calendar1.calendarObjectWithName(name)
- self.assertEquals(calendarObject.component(), component)
+ self.assertEquals((yield calendarObject.component()), component)
yield self.commit()
@@ -771,6 +781,7 @@
)
+ @inlineCallbacks
def test_createCalendarObjectWithName_exists(self):
"""
L{ICalendar.createCalendarObjectWithName} raises
@@ -779,10 +790,9 @@
"""
cal = yield self.calendarUnderTest()
comp = VComponent.fromString(event4_text)
- self.assertRaises(
+ yield self.failUnlessFailure(
+ maybeDeferred(cal.createCalendarObjectWithName, "1.ics", comp),
ObjectResourceNameAlreadyExistsError,
- cal.createCalendarObjectWithName,
- "1.ics", comp
)
@@ -793,10 +803,10 @@
L{InvalidCalendarComponentError} if presented with invalid iCalendar
text.
"""
- self.assertRaises(
+ yield self.failUnlessFailure(
+ maybeDeferred((yield self.calendarUnderTest()).createCalendarObjectWithName,
+ "new", VComponent.fromString(event4notCalDAV_text)),
InvalidObjectResourceError,
- (yield self.calendarUnderTest()).createCalendarObjectWithName,
- "new", VComponent.fromString(event4notCalDAV_text)
)
@inlineCallbacks
@@ -806,10 +816,10 @@
presented with invalid iCalendar text.
"""
calendarObject = yield self.calendarObjectUnderTest()
- self.assertRaises(
+ yield self.failUnlessFailure(
+ maybeDeferred(calendarObject.setComponent,
+ VComponent.fromString(event4notCalDAV_text)),
InvalidObjectResourceError,
- calendarObject.setComponent,
- VComponent.fromString(event4notCalDAV_text)
)
@@ -822,9 +832,9 @@
calendar1 = yield self.calendarUnderTest()
component = VComponent.fromString(event4_text)
calendarObject = yield calendar1.calendarObjectWithName("1.ics")
- self.assertRaises(
+ yield self.failUnlessFailure(
+ maybeDeferred(calendarObject.setComponent, component),
InvalidObjectResourceError,
- calendarObject.setComponent, component
)
@@ -868,12 +878,12 @@
calendarObject = yield calendar1.calendarObjectWithName("1.ics")
oldComponent = calendarObject.component()
self.assertNotEqual(component, oldComponent)
- calendarObject.setComponent(component)
- self.assertEquals(calendarObject.component(), component)
+ yield calendarObject.setComponent(component)
+ self.assertEquals((yield calendarObject.component()), component)
# Also check a new instance
calendarObject = yield calendar1.calendarObjectWithName("1.ics")
- self.assertEquals(calendarObject.component(), component)
+ self.assertEquals((yield calendarObject.component()), component)
yield self.commit()
@@ -927,7 +937,7 @@
calendar object which has been created but not committed.
"""
calendar = yield self.calendarUnderTest()
- calendar.createCalendarObjectWithName(
+ yield calendar.createCalendarObjectWithName(
"4.ics", VComponent.fromString(event4_text)
)
newEvent = yield calendar.calendarObjectWithName("4.ics")
@@ -957,7 +967,7 @@
(yield self.calendarObjectUnderTest()).properties()[propertyName],
propertyContent)
obj = yield self.calendarObjectUnderTest()
- event1_text = obj.iCalendarText()
+ event1_text = yield obj.iCalendarText()
event1_text_withDifferentSubject = event1_text.replace(
"SUMMARY:CalDAV protocol updates",
"SUMMARY:Changed"
@@ -1022,12 +1032,12 @@
-APPLE-DROPBOX property, if available.
"""
cal = yield self.calendarUnderTest()
- cal.createCalendarObjectWithName("drop.ics", VComponent.fromString(
+ yield cal.createCalendarObjectWithName("drop.ics", VComponent.fromString(
self.eventWithDropbox
)
)
obj = yield cal.calendarObjectWithName("drop.ics")
- self.assertEquals(obj.dropboxID(), "some-dropbox-id")
+ self.assertEquals((yield obj.dropboxID()), "some-dropbox-id")
@inlineCallbacks
@@ -1058,7 +1068,9 @@
Common logic for attachment-creation tests.
"""
obj = yield self.calendarObjectUnderTest()
- t = obj.createAttachmentWithName("new.attachment", MimeType("text", "x-fixture"))
+ t = yield obj.createAttachmentWithName(
+ "new.attachment", MimeType("text", "x-fixture")
+ )
t.write("new attachment")
t.write(" text")
t.loseConnection()
@@ -1071,7 +1083,7 @@
self.deferred.callback(self.buf)
capture = CaptureProtocol()
capture.deferred = Deferred()
- attachment = obj.attachmentWithName("new.attachment")
+ attachment = yield obj.attachmentWithName("new.attachment")
self.assertProvides(IAttachment, attachment)
attachment.retrieve(capture)
data = yield capture.deferred
@@ -1081,7 +1093,7 @@
self.assertEquals(contentType, MimeType("text", "x-fixture"))
self.assertEquals(attachment.md5(), '50a9f27aeed9247a0833f30a631f1858')
self.assertEquals(
- [attachment.name() for attachment in obj.attachments()],
+ [attachment.name() for attachment in (yield obj.attachments())],
['new.attachment']
)
@@ -1120,9 +1132,9 @@
obj.removeAttachmentWithName("new.attachment")
obj = yield refresh(obj)
self.assertIdentical(
- None, obj.attachmentWithName("new.attachment")
+ None, (yield obj.attachmentWithName("new.attachment"))
)
- self.assertEquals(list(obj.attachments()), [])
+ self.assertEquals(list((yield obj.attachments())), [])
return self.test_createAttachmentCommit().addCallback(deleteIt)
@@ -1147,8 +1159,9 @@
L{ICalendarHome.calendarWithName} or L{ICalendarHome.calendars}.
"""
obj = yield self.calendarObjectUnderTest()
- t = obj.createAttachmentWithName("new.attachment",
- MimeType("text", "plain"))
+ t = yield obj.createAttachmentWithName(
+ "new.attachment", MimeType("text", "plain")
+ )
t.write("new attachment text")
t.loseConnection()
yield self.commit()
@@ -1170,8 +1183,15 @@
yield self.calendarObjectUnderTest()
txn = self.lastTransaction
yield self.commit()
- self.assertRaises(AlreadyFinishedError, txn.commit)
- self.assertRaises(AlreadyFinishedError, txn.abort)
+
+ yield self.failUnlessFailure(
+ maybeDeferred(txn.commit),
+ AlreadyFinishedError
+ )
+ yield self.failUnlessFailure(
+ maybeDeferred(txn.abort),
+ AlreadyFinishedError
+ )
@inlineCallbacks
@@ -1224,7 +1244,7 @@
yield self.commit()
foundUIDs = set([])
lastTxn = None
- for txn, home in self.storeUnderTest().eachCalendarHome():
+ for txn, home in (yield self.storeUnderTest().eachCalendarHome()):
self.addCleanup(txn.commit)
foundUIDs.add(home.uid())
self.assertNotIdentical(lastTxn, txn)
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_sql.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_sql.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -19,19 +19,18 @@
L{txdav.caldav.datastore.test.common}.
"""
-import time
+from twisted.trial import unittest
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.task import deferLater
+from twisted.internet import reactor
+from twext.python.vcomponent import VComponent
+from twext.web2.dav.element.rfc2518 import GETContentLanguage, ResourceType
+
from txdav.caldav.datastore.test.common import CommonTests as CalendarCommonTests
-
from txdav.common.datastore.sql import ECALENDARTYPE
from txdav.common.datastore.test.util import buildStore, populateCalendarsFrom
-from twisted.trial import unittest
-from twisted.internet.defer import inlineCallbacks
-from twisted.internet.threads import deferToThread
-from twext.python.vcomponent import VComponent
-from twext.web2.dav.element.rfc2518 import GETContentLanguage, ResourceType
-
from txdav.caldav.datastore.test.test_file import setUpCalendarStore
from txdav.caldav.datastore.util import _migrateCalendar, migrateHome
from txdav.base.propertystore.base import PropertyName
@@ -63,19 +62,24 @@
return self._sqlCalendarStore
+ @inlineCallbacks
def assertCalendarsSimilar(self, a, b, bCalendarFilter=None):
"""
Assert that two calendars have a similar structure (contain the same
events).
"""
- def namesAndComponents(x, filter=lambda x:x.component()):
- return dict([(fromObj.name(), filter(fromObj))
- for fromObj in x.calendarObjects()])
+ @inlineCallbacks
+ def namesAndComponents(x, filter=lambda x: x.component()):
+ result = {}
+ for fromObj in (yield x.calendarObjects()):
+ result[fromObj.name()] = yield filter(fromObj)
+ returnValue(result)
if bCalendarFilter is not None:
extra = [bCalendarFilter]
else:
extra = []
- self.assertEquals(namesAndComponents(a), namesAndComponents(b, *extra))
+ self.assertEquals((yield namesAndComponents(a)),
+ (yield namesAndComponents(b, *extra)))
def assertPropertiesSimilar(self, a, b, disregard=[]):
@@ -104,6 +108,7 @@
self.addCleanup(txn.commit)
return txn
+
@inlineCallbacks
def test_attachmentPath(self):
"""
@@ -112,7 +117,9 @@
L{ICalendarObject.attachmentWithName}.
"""
yield self.createAttachmentTest(lambda x: x)
- attachmentRoot = (yield self.calendarObjectUnderTest())._txn._store.attachmentsPath
+ attachmentRoot = (
+ yield self.calendarObjectUnderTest()
+ )._txn._store.attachmentsPath
attachmentPath = attachmentRoot.child("ho").child("me").child("home1")
attachmentPath = attachmentPath.child(
(yield self.calendarObjectUnderTest()).uid()).child(
@@ -131,8 +138,9 @@
toHome = yield self.transactionUnderTest().calendarHomeWithUID(
"new-home", create=True)
toCalendar = yield toHome.calendarWithName("calendar")
- _migrateCalendar(fromCalendar, toCalendar, lambda x: x.component())
- self.assertCalendarsSimilar(fromCalendar, toCalendar)
+ yield _migrateCalendar(fromCalendar, toCalendar,
+ lambda x: x.component())
+ yield self.assertCalendarsSimilar(fromCalendar, toCalendar)
@inlineCallbacks
@@ -157,7 +165,7 @@
toHome = yield self.transactionUnderTest().calendarHomeWithUID(
"new-home", create=True
)
- migrateHome(fromHome, toHome, lambda x: x.component())
+ yield migrateHome(fromHome, toHome, lambda x: x.component())
toCalendars = yield toHome.calendars()
self.assertEquals(set([c.name() for c in toCalendars]),
set([k for k in self.requirements['home1'].keys()
@@ -183,55 +191,58 @@
"stubbed out, as migration only needs to go from file->sql currently")
-
-
@inlineCallbacks
def test_homeProvisioningConcurrency(self):
"""
- Test that two concurrent attempts to provision a calendar home do not cause a race-condition
- whereby the second commit results in a second INSERT that violates a unique constraint. Also verify
- that, whilst the two provisioning attempts are happening and doing various lock operations, that we
- do not block other reads of the table.
+ Test that two concurrent attempts to provision a calendar home do not
+ cause a race-condition whereby the second commit results in a second
+ C{INSERT} that violates a unique constraint. Also verify that, while
+ the two provisioning attempts are happening and doing various lock
+ operations, that we do not block other reads of the table.
"""
- calendarStore1 = yield buildStore(self, self.notifierFactory)
- calendarStore2 = yield buildStore(self, self.notifierFactory)
- calendarStore3 = yield buildStore(self, self.notifierFactory)
+ calendarStore = yield buildStore(self, self.notifierFactory)
- txn1 = calendarStore1.newTransaction()
- txn2 = calendarStore2.newTransaction()
- txn3 = calendarStore3.newTransaction()
-
- # Provision one home now - we will use this to later verify we can do reads of
- # existing data in the table
- home_uid2 = txn3.homeWithUID(ECALENDARTYPE, "uid2", create=True)
+ txn1 = calendarStore.newTransaction()
+ txn2 = calendarStore.newTransaction()
+ txn3 = calendarStore.newTransaction()
+
+ # Provision one home now - we will use this to later verify we can do
+ # reads of existing data in the table
+ home_uid2 = yield txn3.homeWithUID(ECALENDARTYPE, "uid2", create=True)
self.assertNotEqual(home_uid2, None)
txn3.commit()
- home_uid1_1 = txn1.homeWithUID(ECALENDARTYPE, "uid1", create=True)
-
+ home_uid1_1 = yield txn1.homeWithUID(
+ ECALENDARTYPE, "uid1", create=True
+ )
+
+ @inlineCallbacks
def _defer_home_uid1_2():
- home_uid1_2 = txn2.homeWithUID(ECALENDARTYPE, "uid1", create=True)
- txn2.commit()
- return home_uid1_2
- d1 = deferToThread(_defer_home_uid1_2)
-
+ home_uid1_2 = yield txn2.homeWithUID(
+ ECALENDARTYPE, "uid1", create=True
+ )
+ yield txn2.commit()
+ returnValue(home_uid1_2)
+ d1 = _defer_home_uid1_2()
+
+ @inlineCallbacks
def _pause_home_uid1_1():
- time.sleep(1)
- txn1.commit()
- d2 = deferToThread(_pause_home_uid1_1)
-
+ yield deferLater(reactor, 1.0, lambda : None)
+ yield txn1.commit()
+ d2 = _pause_home_uid1_1()
+
# Verify that we can still get to the existing home - i.e. the lock
# on the table allows concurrent reads
- txn4 = calendarStore3.newTransaction()
- home_uid2 = txn4.homeWithUID(ECALENDARTYPE, "uid2", create=True)
+ txn4 = calendarStore.newTransaction()
+ home_uid2 = yield txn4.homeWithUID(ECALENDARTYPE, "uid2", create=True)
self.assertNotEqual(home_uid2, None)
txn4.commit()
-
+
# Now do the concurrent provision attempt
yield d2
home_uid1_2 = yield d1
-
+
self.assertNotEqual(home_uid1_1, None)
self.assertNotEqual(home_uid1_2, None)
@@ -248,21 +259,22 @@
# Provision the home now
txn = calendarStore1.newTransaction()
- home = txn.homeWithUID(ECALENDARTYPE, "uid1", create=True)
+ home = yield txn.homeWithUID(ECALENDARTYPE, "uid1", create=True)
self.assertNotEqual(home, None)
txn.commit()
txn1 = calendarStore1.newTransaction()
txn2 = calendarStore2.newTransaction()
- home1 = txn1.homeWithUID(ECALENDARTYPE, "uid1", create=True)
- home2 = txn2.homeWithUID(ECALENDARTYPE, "uid1", create=True)
-
+ home1 = yield txn1.homeWithUID(ECALENDARTYPE, "uid1", create=True)
+ home2 = yield txn2.homeWithUID(ECALENDARTYPE, "uid1", create=True)
+
adbk1 = yield home1.calendarWithName("calendar")
adbk2 = yield home2.calendarWithName("calendar")
-
+
+ @inlineCallbacks
def _defer1():
- adbk1.createObjectResourceWithName("1.ics", VComponent.fromString(
+ yield adbk1.createObjectResourceWithName("1.ics", VComponent.fromString(
"BEGIN:VCALENDAR\r\n"
"VERSION:2.0\r\n"
"PRODID:-//Apple Inc.//iCal 4.0.1//EN\r\n"
@@ -302,11 +314,12 @@
"END:VEVENT\r\n"
"END:VCALENDAR\r\n"
))
- txn1.commit()
- d1 = deferToThread(_defer1)
-
+ yield txn1.commit()
+ d1 = _defer1()
+
+ @inlineCallbacks
def _defer2():
- adbk2.createObjectResourceWithName("2.ics", VComponent.fromString(
+ yield adbk2.createObjectResourceWithName("2.ics", VComponent.fromString(
"BEGIN:VCALENDAR\r\n"
"VERSION:2.0\r\n"
"PRODID:-//Apple Inc.//iCal 4.0.1//EN\r\n"
@@ -346,8 +359,8 @@
"END:VEVENT\r\n"
"END:VCALENDAR\r\n"
))
- txn2.commit()
- d2 = deferToThread(_defer2)
+ yield txn2.commit()
+ d2 = _defer2()
yield d1
yield d2
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_util.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_util.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/test/test_util.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -18,6 +18,7 @@
Tests for txdav.caldav.datastore.util.
"""
+from twisted.internet.defer import inlineCallbacks
from twistedcaldav.ical import Component
from twistedcaldav.test.util import TestCase
from txdav.caldav.datastore.util import dropboxIDFromCalendarObject
@@ -31,19 +32,20 @@
"""
Fake object resource to work with tests.
"""
-
+
def __init__(self, data):
-
+
self.ical = Component.fromString(data)
-
+
def component(self):
return self.ical
-
+
def uid(self):
return self.ical.resourceUID()
+
+ @inlineCallbacks
def test_noAttachOrXdash(self):
-
resource = DropboxIDTests.FakeCalendarResource("""BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
@@ -54,9 +56,14 @@
END:VEVENT
END:VCALENDAR
""")
-
- self.assertEquals(dropboxIDFromCalendarObject(resource), "12345-67890.dropbox")
+ self.assertEquals(
+ (yield dropboxIDFromCalendarObject(resource)),
+ "12345-67890.dropbox"
+ )
+
+
+ @inlineCallbacks
def test_okXdash(self):
resource = DropboxIDTests.FakeCalendarResource("""BEGIN:VCALENDAR
@@ -70,11 +77,15 @@
END:VEVENT
END:VCALENDAR
""")
-
- self.assertEquals(dropboxIDFromCalendarObject(resource), "12345-67890X.dropbox")
+ self.assertEquals(
+ (yield dropboxIDFromCalendarObject(resource)),
+ "12345-67890X.dropbox"
+ )
+
+
+ @inlineCallbacks
def test_badXdash(self):
-
resource = DropboxIDTests.FakeCalendarResource("""BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
@@ -86,9 +97,11 @@
END:VEVENT
END:VCALENDAR
""")
-
- self.assertEquals(dropboxIDFromCalendarObject(resource), "")
+ self.assertEquals( (yield dropboxIDFromCalendarObject(resource)), "")
+
+
+ @inlineCallbacks
def test_okAttach(self):
resource = DropboxIDTests.FakeCalendarResource("""BEGIN:VCALENDAR
@@ -102,9 +115,14 @@
END:VEVENT
END:VCALENDAR
""")
-
- self.assertEquals(dropboxIDFromCalendarObject(resource), "12345-67890Y.dropbox")
+ self.assertEquals(
+ (yield dropboxIDFromCalendarObject(resource)),
+ "12345-67890Y.dropbox"
+ )
+
+
+ @inlineCallbacks
def test_badAttach(self):
resource = DropboxIDTests.FakeCalendarResource("""BEGIN:VCALENDAR
@@ -118,9 +136,14 @@
END:VEVENT
END:VCALENDAR
""")
-
- self.assertEquals(dropboxIDFromCalendarObject(resource), "12345-67890.dropbox")
+ self.assertEquals(
+ (yield dropboxIDFromCalendarObject(resource)),
+ "12345-67890.dropbox"
+ )
+
+
+ @inlineCallbacks
def test_inlineAttach(self):
resource = DropboxIDTests.FakeCalendarResource("""BEGIN:VCALENDAR
@@ -134,9 +157,14 @@
END:VEVENT
END:VCALENDAR
""")
-
- self.assertEquals(dropboxIDFromCalendarObject(resource), "12345-67890.dropbox")
+ self.assertEquals(
+ (yield dropboxIDFromCalendarObject(resource)),
+ "12345-67890.dropbox"
+ )
+
+
+ @inlineCallbacks
def test_multipleAttach(self):
resource = DropboxIDTests.FakeCalendarResource("""BEGIN:VCALENDAR
@@ -152,9 +180,14 @@
END:VEVENT
END:VCALENDAR
""")
-
- self.assertEquals(dropboxIDFromCalendarObject(resource), "12345-67890Z.dropbox")
+ self.assertEquals(
+ (yield dropboxIDFromCalendarObject(resource)),
+ "12345-67890Z.dropbox"
+ )
+
+
+ @inlineCallbacks
def test_okAttachRecurring(self):
resource = DropboxIDTests.FakeCalendarResource("""BEGIN:VCALENDAR
@@ -176,10 +209,14 @@
END:VEVENT
END:VCALENDAR
""")
-
- self.assertEquals(dropboxIDFromCalendarObject(resource), "12345-67890Y.dropbox")
+ self.assertEquals(
+ (yield dropboxIDFromCalendarObject(resource)),
+ "12345-67890Y.dropbox"
+ )
+
+ @inlineCallbacks
def test_okAttachAlarm(self):
resource = DropboxIDTests.FakeCalendarResource("""BEGIN:VCALENDAR
@@ -198,6 +235,10 @@
END:VEVENT
END:VCALENDAR
""")
-
- self.assertEquals(dropboxIDFromCalendarObject(resource), "12345-67890.dropbox")
+ self.assertEquals(
+ (yield dropboxIDFromCalendarObject(resource)),
+ "12345-67890.dropbox"
+ )
+
+
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/util.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/util.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/caldav/datastore/util.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -1,3 +1,4 @@
+# -*- test-case-name: txdav.caldav.datastore.test.test_util -*-
##
# Copyright (c) 2010 Apple Inc. All rights reserved.
#
@@ -18,7 +19,7 @@
Utility logic common to multiple backend implementations.
"""
-from twisted.internet.defer import inlineCallbacks, Deferred
+from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
from twisted.internet.protocol import Protocol
from twext.python.vcomponent import InvalidICalendarDataError
@@ -69,6 +70,7 @@
raise InvalidObjectResourceError(e)
+ at inlineCallbacks
def dropboxIDFromCalendarObject(calendarObject):
"""
Helper to implement L{ICalendarObject.dropboxID}.
@@ -78,16 +80,17 @@
"""
# Try "X-APPLE-DROPBOX" first
- dropboxProperty = calendarObject.component(
- ).getFirstPropertyInAnyComponent("X-APPLE-DROPBOX")
+ dropboxProperty = (yield calendarObject.component(
+ )).getFirstPropertyInAnyComponent("X-APPLE-DROPBOX")
if dropboxProperty is not None:
componentDropboxID = dropboxProperty.value().split("/")[-1]
- return componentDropboxID
+ returnValue(componentDropboxID)
# Now look at each ATTACH property and see if it might be a dropbox item
# and if so extract the id from that
- attachments = calendarObject.component().getAllPropertiesInAnyComponent(
+ attachments = (yield calendarObject.component()
+ ).getAllPropertiesInAnyComponent(
"ATTACH",
depth=1,
)
@@ -99,11 +102,11 @@
segments = attachment.value().split("/")
try:
if segments[-3] == "dropbox":
- return segments[-2]
+ returnValue(segments[-2])
except IndexError:
pass
- return calendarObject.uid() + ".dropbox"
+ returnValue(calendarObject.uid() + ".dropbox")
@inlineCallbacks
@@ -122,10 +125,10 @@
for calendarObject in (yield inCalendar.calendarObjects()):
try:
- outCalendar.createCalendarObjectWithName(
+ yield outCalendar.createCalendarObjectWithName(
calendarObject.name(),
- calendarObject.component()) # XXX WRONG SHOULD CALL getComponent
-
+ (yield calendarObject.component())) # XXX WRONG SHOULD CALL getComponent
+
# Only the owner's properties are migrated, since previous releases of
# calendar server didn't have per-user properties.
outObject = yield outCalendar.calendarObjectWithName(
@@ -133,10 +136,10 @@
outObject.properties().update(calendarObject.properties())
# Migrate attachments.
- for attachment in calendarObject.attachments():
+ for attachment in (yield calendarObject.attachments()):
name = attachment.name()
ctype = attachment.contentType()
- transport = outObject.createAttachmentWithName(name, ctype)
+ transport = yield outObject.createAttachmentWithName(name, ctype)
proto =_AttachmentMigrationProto(transport)
attachment.retrieve(proto)
yield proto.done
@@ -184,15 +187,15 @@
(from a calendar in C{inHome}) and returns a L{VComponent} (to store in
a calendar in outHome).
"""
- outHome.removeCalendarWithName("calendar")
- outHome.removeCalendarWithName("inbox")
+ yield outHome.removeCalendarWithName("calendar")
+ yield outHome.removeCalendarWithName("inbox")
outHome.properties().update(inHome.properties())
inCalendars = yield inHome.calendars()
for calendar in inCalendars:
name = calendar.name()
if name == "outbox":
continue
- outHome.createCalendarWithName(name)
+ yield outHome.createCalendarWithName(name)
outCalendar = yield outHome.calendarWithName(name)
try:
yield _migrateCalendar(calendar, outCalendar, getComponent)
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/sql.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/sql.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -14,6 +14,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
##
+from twisted.internet.defer import inlineCallbacks, returnValue
__all__ = [
"AddressBookHome",
@@ -44,6 +45,8 @@
from zope.interface.declarations import implements
+
+
class AddressBookHome(CommonHome):
implements(IAddressBookHome)
@@ -58,15 +61,19 @@
super(AddressBookHome, self).__init__(transaction, ownerUID, resourceID, notifier)
self._shares = SQLLegacyAddressBookShares(self)
+
addressbooks = CommonHome.children
listAddressbooks = CommonHome.listChildren
addressbookWithName = CommonHome.childWithName
createAddressBookWithName = CommonHome.createChildWithName
removeAddressBookWithName = CommonHome.removeChildWithName
+
def createdHome(self):
- self.createAddressBookWithName("addressbook")
+ return self.createAddressBookWithName("addressbook")
+
+
class AddressBook(CommonHomeChild):
"""
File-based implementation of L{IAddressBook}.
@@ -99,13 +106,16 @@
self._revisionsTable = ADDRESSBOOK_OBJECT_REVISIONS_TABLE
self._objectTable = ADDRESSBOOK_OBJECT_TABLE
+
@property
def _addressbookHome(self):
return self._home
+
def resourceType(self):
return ResourceType.addressbook #@UndefinedVariable
+
ownerAddressBookHome = CommonHomeChild.ownerHome
addressbookObjects = CommonHomeChild.objectResources
listAddressbookObjects = CommonHomeChild.listObjectResources
@@ -128,36 +138,43 @@
),
)
+
def _doValidate(self, component):
component.validForCardDAV()
+
def contentType(self):
"""
The content type of Addresbook objects is text/vcard.
"""
return MimeType.fromString("text/vcard; charset=utf-8")
+
+
class AddressBookObject(CommonObjectResource):
implements(IAddressBookObject)
- def __init__(self, name, addressbook, resid):
+ def __init__(self, name, addressbook, resid, uid):
- super(AddressBookObject, self).__init__(name, addressbook, resid)
-
+ super(AddressBookObject, self).__init__(name, addressbook, resid, uid)
self._objectTable = ADDRESSBOOK_OBJECT_TABLE
+
@property
def _addressbook(self):
return self._parentCollection
+
def addressbook(self):
return self._addressbook
+
+ @inlineCallbacks
def setComponent(self, component, inserting=False):
validateAddressBookComponent(self, self._addressbook, component, inserting)
- self.updateDatabase(component, inserting=inserting)
+ yield self.updateDatabase(component, inserting=inserting)
if inserting:
self._addressbook._insertRevision(self._name)
else:
@@ -165,6 +182,8 @@
self._addressbook.notifyChanged()
+
+ @inlineCallbacks
def updateDatabase(self, component, expand_until=None, reCreate=False, inserting=False):
"""
Update the database tables for the new data being written.
@@ -178,7 +197,7 @@
# ADDRESSBOOK_OBJECT table update
if inserting:
- self._resourceID = self._txn.execSQL(
+ self._resourceID = (yield self._txn.execSQL(
"""
insert into ADDRESSBOOK_OBJECT
(ADDRESSBOOK_RESOURCE_ID, RESOURCE_NAME, VCARD_TEXT, VCARD_UID)
@@ -192,9 +211,9 @@
componentText,
component.resourceUID(),
]
- )[0][0]
+ ))[0][0]
else:
- self._txn.execSQL(
+ yield self._txn.execSQL(
"""
update ADDRESSBOOK_OBJECT set
(VCARD_TEXT, VCARD_UID, MODIFIED)
@@ -209,31 +228,15 @@
]
)
+
+ @inlineCallbacks
def component(self):
- return VCard.fromString(self.vCardText())
+ returnValue(VCard.fromString((yield self.vCardText())))
- def text(self):
- if self._objectText is None:
- text = self._txn.execSQL(
- "select VCARD_TEXT from ADDRESSBOOK_OBJECT where "
- "RESOURCE_ID = %s", [self._resourceID]
- )[0][0]
- self._objectText = text
- return text
- else:
- return self._objectText
- vCardText = text
+ vCardText = CommonObjectResource.text
- def uid(self):
- return self.component().resourceUID()
- def name(self):
- return self._name
-
- def componentType(self):
- return self.component().mainType()
-
# IDataStoreResource
def contentType(self):
"""
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/common.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/common.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/common.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -17,7 +17,7 @@
"""
Tests for common addressbook store API functions.
"""
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, maybeDeferred
from txdav.idav import IPropertyStore, IDataStore
from txdav.base.propertystore.base import PropertyName
@@ -178,12 +178,13 @@
self.assertProvides(IAddressBookTransaction, txn)
+ @inlineCallbacks
def test_homeProvides(self):
"""
The addressbook homes generated by the addressbook store provide
L{IAddressBookHome} and its required attributes.
"""
- self.assertProvides(IAddressBookHome, self.homeUnderTest())
+ self.assertProvides(IAddressBookHome, (yield self.homeUnderTest()))
@inlineCallbacks
@@ -207,7 +208,7 @@
@inlineCallbacks
def test_notifierID(self):
- home = self.homeUnderTest()
+ home = yield self.homeUnderTest()
self.assertEquals(home.notifierID(), "CardDAV|home1")
addressbook = yield home.addressbookWithName("addressbook_1")
self.assertEquals(addressbook.notifierID(), "CardDAV|home1")
@@ -243,7 +244,7 @@
L{IAddressBookHome.addressbookWithName} returns an L{IAddressBook} provider,
whose name matches the one passed in.
"""
- home = self.homeUnderTest()
+ home = yield self.homeUnderTest()
for name in home1_addressbookNames:
addressbook = yield home.addressbookWithName(name)
if addressbook is None:
@@ -257,9 +258,9 @@
"""
L{IAddressBook.rename} changes the name of the L{IAddressBook}.
"""
- home = self.homeUnderTest()
+ home = yield self.homeUnderTest()
addressbook = yield home.addressbookWithName("addressbook_1")
- addressbook.rename("some_other_name")
+ yield addressbook.rename("some_other_name")
@inlineCallbacks
def positiveAssertions():
self.assertEquals(addressbook.name(), "some_other_name")
@@ -292,10 +293,10 @@
L{IAddressBookHome.createAddressBookWithName} creates a new L{IAddressBook} that
can be retrieved with L{IAddressBookHome.addressbookWithName}.
"""
- home = self.homeUnderTest()
+ home = yield self.homeUnderTest()
name = "new"
self.assertIdentical((yield home.addressbookWithName(name)), None)
- home.createAddressBookWithName(name)
+ yield home.createAddressBookWithName(name)
self.assertNotIdentical((yield home.addressbookWithName(name)), None)
def checkProperties():
addressbookProperties = (yield home.addressbookWithName(name)).properties()
@@ -314,7 +315,7 @@
[("update", "CardDAV|home1")])
# Make sure it's available in a new transaction; i.e. test the commit.
- home = self.homeUnderTest()
+ home = yield self.homeUnderTest()
self.assertNotIdentical((yield home.addressbookWithName(name)), None)
# FIXME: These two lines aren't in the calendar common tests:
@@ -326,6 +327,7 @@
checkProperties()
+ @inlineCallbacks
def test_createAddressBookWithName_exists(self):
"""
L{IAddressBookHome.createAddressBookWithName} raises
@@ -333,9 +335,10 @@
existing address book.
"""
for name in home1_addressbookNames:
- self.assertRaises(
+ yield self.failUnlessFailure(
+ maybeDeferred(
+ (yield self.homeUnderTest()).createAddressBookWithName, name),
HomeChildNameAlreadyExistsError,
- self.homeUnderTest().createAddressBookWithName, name
)
@@ -345,11 +348,11 @@
L{IAddressBookHome.removeAddressBookWithName} removes a addressbook that already
exists.
"""
- home = self.homeUnderTest()
+ home = yield self.homeUnderTest()
# FIXME: test transactions
for name in home1_addressbookNames:
self.assertNotIdentical((yield home.addressbookWithName(name)), None)
- home.removeAddressBookWithName(name)
+ yield home.removeAddressBookWithName(name)
self.assertEquals((yield home.addressbookWithName(name)), None)
yield self.commit()
@@ -368,13 +371,16 @@
)
+ @inlineCallbacks
def test_removeAddressBookWithName_absent(self):
"""
Attempt to remove an non-existing addressbook should raise.
"""
- home = self.homeUnderTest()
- self.assertRaises(NoSuchHomeChildError,
- home.removeAddressBookWithName, "xyzzy")
+ home = yield self.homeUnderTest()
+ yield self.failUnlessFailure(
+ maybeDeferred(home.removeAddressBookWithName, "xyzzy"),
+ NoSuchHomeChildError
+ )
@inlineCallbacks
@@ -460,7 +466,7 @@
(yield addressbook.addressbookObjectWithUID(uid)),
None
)
- addressbook.removeAddressBookObjectWithUID(uid)
+ yield addressbook.removeAddressBookObjectWithUID(uid)
self.assertEquals(
(yield addressbook.addressbookObjectWithUID(uid)),
None
@@ -481,7 +487,7 @@
self.assertNotIdentical(
(yield addressbook.addressbookObjectWithName(name)), None
)
- addressbook.removeAddressBookObjectWithName(name)
+ yield addressbook.removeAddressBookObjectWithName(name)
self.assertIdentical(
(yield addressbook.addressbookObjectWithName(name)), None
)
@@ -507,9 +513,9 @@
Attempt to remove an non-existing addressbook object should raise.
"""
addressbook = yield self.addressbookUnderTest()
- self.assertRaises(
- NoSuchObjectResourceError,
- addressbook.removeAddressBookObjectWithName, "xyzzy"
+ yield self.failUnlessFailure(
+ maybeDeferred(addressbook.removeAddressBookObjectWithName, "xyzzy"),
+ NoSuchObjectResourceError
)
@@ -537,7 +543,7 @@
L{IAddressBookObject.component} returns a L{VComponent} describing the
addressbook data underlying that addressbook object.
"""
- component = (yield self.addressbookObjectUnderTest()).component()
+ component = yield (yield self.addressbookObjectUnderTest()).component()
self.failUnless(
isinstance(component, VComponent),
@@ -554,7 +560,7 @@
L{IAddressBookObject.iAddressBookText} returns a C{str} describing the same
data provided by L{IAddressBookObject.component}.
"""
- text = (yield self.addressbookObjectUnderTest()).vCardText()
+ text = yield (yield self.addressbookObjectUnderTest()).vCardText()
self.assertIsInstance(text, str)
self.failUnless(text.startswith("BEGIN:VCARD\r\n"))
self.assertIn("\r\nUID:uid1\r\n", text)
@@ -614,10 +620,10 @@
L{IAddressBookHome.addressbooks} includes addressbooks recently added with
L{IAddressBookHome.createAddressBookWithName}.
"""
- home = self.homeUnderTest()
+ home = yield self.homeUnderTest()
allAddressbooks = yield home.addressbooks()
before = set(x.name() for x in allAddressbooks)
- home.createAddressBookWithName("new-name")
+ yield home.createAddressBookWithName("new-name")
allAddressbooks = yield home.addressbooks()
after = set(x.name() for x in allAddressbooks)
self.assertEquals(before | set(['new-name']), after)
@@ -633,10 +639,10 @@
name = "4.vcf"
self.assertIdentical((yield addressbook1.addressbookObjectWithName(name)), None)
component = VComponent.fromString(vcard4_text)
- addressbook1.createAddressBookObjectWithName(name, component)
+ yield addressbook1.createAddressBookObjectWithName(name, component)
addressbookObject = yield addressbook1.addressbookObjectWithName(name)
- self.assertEquals(addressbookObject.component(), component)
+ self.assertEquals((yield addressbookObject.component()), component)
yield self.commit()
@@ -657,10 +663,11 @@
L{AddressBookObjectNameAlreadyExistsError} if a addressbook object with the
given name already exists in that addressbook.
"""
- self.assertRaises(
- ObjectResourceNameAlreadyExistsError,
- (yield self.addressbookUnderTest()).createAddressBookObjectWithName,
- "1.vcf", VComponent.fromString(vcard4_text)
+ self.failUnlessFailure(
+ maybeDeferred((yield self.addressbookUnderTest())
+ .createAddressBookObjectWithName,
+ "1.vcf", VComponent.fromString(vcard4_text)),
+ ObjectResourceNameAlreadyExistsError
)
@@ -671,10 +678,11 @@
L{InvalidAddressBookComponentError} if presented with invalid iAddressBook
text.
"""
- self.assertRaises(
- InvalidObjectResourceError,
- (yield self.addressbookUnderTest()).createAddressBookObjectWithName,
- "new", VComponent.fromString(vcard4notCardDAV_text)
+ yield self.failUnlessFailure(
+ maybeDeferred((yield self.addressbookUnderTest())
+ .createAddressBookObjectWithName,
+ "new", VComponent.fromString(vcard4notCardDAV_text)),
+ InvalidObjectResourceError
)
@@ -685,33 +693,34 @@
presented with invalid iAddressBook text.
"""
addressbookObject = (yield self.addressbookObjectUnderTest())
- self.assertRaises(
- InvalidObjectResourceError,
- addressbookObject.setComponent,
- VComponent.fromString(vcard4notCardDAV_text)
+ yield self.failUnlessFailure(
+ maybeDeferred(addressbookObject.setComponent,
+ VComponent.fromString(vcard4notCardDAV_text)),
+ InvalidObjectResourceError
)
@inlineCallbacks
def test_setComponent_uidchanged(self):
"""
- L{IAddressBookObject.setComponent} raises L{InvalidAddressBookComponentError}
- when given a L{VComponent} whose UID does not match its existing UID.
+ L{IAddressBookObject.setComponent} raises
+ L{InvalidAddressBookComponentError} when given a L{VComponent} whose
+ UID does not match its existing UID.
"""
addressbook1 = yield self.addressbookUnderTest()
component = VComponent.fromString(vcard4_text)
addressbookObject = yield addressbook1.addressbookObjectWithName("1.vcf")
- self.assertRaises(
- InvalidObjectResourceError,
- addressbookObject.setComponent, component
+ yield self.failUnlessFailure(
+ maybeDeferred(addressbookObject.setComponent, component),
+ InvalidObjectResourceError
)
@inlineCallbacks
def test_addressbookHomeWithUID_create(self):
"""
- L{IAddressBookStoreTransaction.addressbookHomeWithUID} with C{create=True}
- will create a addressbook home that doesn't exist yet.
+ L{IAddressBookStoreTransaction.addressbookHomeWithUID} with
+ C{create=True} will create a addressbook home that doesn't exist yet.
"""
txn = self.transactionUnderTest()
noHomeUID = "xyzzy"
@@ -742,14 +751,14 @@
addressbook1 = yield self.addressbookUnderTest()
addressbookObject = yield addressbook1.addressbookObjectWithName("1.vcf")
- oldComponent = addressbookObject.component()
+ oldComponent = yield addressbookObject.component()
self.assertNotEqual(component, oldComponent)
- addressbookObject.setComponent(component)
- self.assertEquals(addressbookObject.component(), component)
+ yield addressbookObject.setComponent(component)
+ self.assertEquals((yield addressbookObject.component()), component)
# Also check a new instance
addressbookObject = yield addressbook1.addressbookObjectWithName("1.vcf")
- self.assertEquals(addressbookObject.component(), component)
+ self.assertEquals((yield addressbookObject.component()), component)
yield self.commit()
@@ -762,6 +771,7 @@
]
)
+
def checkPropertiesMethod(self, thunk):
"""
Verify that the given object has a properties method that returns an
@@ -771,11 +781,12 @@
self.assertProvides(IPropertyStore, properties)
+ @inlineCallbacks
def test_homeProperties(self):
"""
L{IAddressBookHome.properties} returns a property store.
"""
- self.checkPropertiesMethod(self.homeUnderTest())
+ self.checkPropertiesMethod((yield self.homeUnderTest()))
@inlineCallbacks
@@ -801,7 +812,7 @@
addressbook object which has been created but not committed.
"""
addressbook = yield self.addressbookUnderTest()
- addressbook.createAddressBookObjectWithName(
+ yield addressbook.createAddressBookObjectWithName(
"4.vcf", VComponent.fromString(vcard4_text)
)
newEvent = yield addressbook.addressbookObjectWithName("4.vcf")
@@ -833,7 +844,7 @@
],
propertyContent)
obj = yield self.addressbookObjectUnderTest()
- vcard1_text = obj.vCardText()
+ vcard1_text = yield obj.vCardText()
vcard1_text_withDifferentNote = vcard1_text.replace(
"NOTE:CardDAV protocol updates",
"NOTE:Changed"
@@ -870,7 +881,7 @@
Addressbook objects in one user's addressbook should not show up in another
user's via uid or name queries.
"""
- home1 = self.homeUnderTest()
+ home1 = yield self.homeUnderTest()
home2 = yield self.transactionUnderTest().addressbookHomeWithUID(
"home2", create=True)
addressbook1 = yield home1.addressbookWithName("addressbook_1")
@@ -899,7 +910,7 @@
yield self.commit()
foundUIDs = set([])
lastTxn = None
- for txn, home in self.storeUnderTest().eachAddressbookHome():
+ for txn, home in (yield self.storeUnderTest().eachAddressbookHome()):
self.addCleanup(txn.commit)
foundUIDs.add(home.uid())
self.assertNotIdentical(lastTxn, txn)
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_sql.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/test/test_sql.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -19,8 +19,6 @@
L{txdav.carddav.datastore.test.common}.
"""
-import time
-
from txdav.carddav.datastore.test.common import CommonTests as AddressBookCommonTests
from txdav.common.datastore.sql import EADDRESSBOOKTYPE
@@ -32,7 +30,7 @@
from twisted.trial import unittest
from twisted.internet.defer import inlineCallbacks, returnValue
-from twisted.internet.threads import deferToThread
+
from twistedcaldav.vcard import Component as VCard
@@ -56,15 +54,15 @@
home = yield populateTxn.addressbookHomeWithUID(homeUID, True)
# We don't want the default addressbook to appear unless it's
# explicitly listed.
- home.removeAddressBookWithName("addressbook")
+ yield home.removeAddressBookWithName("addressbook")
for addressbookName in addressbooks:
addressbookObjNames = addressbooks[addressbookName]
if addressbookObjNames is not None:
- home.createAddressBookWithName(addressbookName)
- addressbook = home.addressbookWithName(addressbookName)
+ yield home.createAddressBookWithName(addressbookName)
+ addressbook = yield home.addressbookWithName(addressbookName)
for objectName in addressbookObjNames:
objData = addressbookObjNames[objectName]
- addressbook.createAddressBookObjectWithName(
+ yield addressbook.createAddressBookObjectWithName(
objectName, VCard.fromString(objData)
)
@@ -89,7 +87,7 @@
@inlineCallbacks
def namesAndComponents(x, filter=lambda x:x.component()):
fromObjs = yield x.addressbookObjects()
- returnValue(dict([(fromObj.name(), filter(fromObj))
+ returnValue(dict([(fromObj.name(), (yield filter(fromObj)))
for fromObj in fromObjs]))
if bAddressbookFilter is not None:
extra = [bAddressbookFilter]
@@ -137,8 +135,8 @@
toHome = yield self.transactionUnderTest().addressbookHomeWithUID(
"new-home", create=True)
toAddressbook = yield toHome.addressbookWithName("addressbook")
- _migrateAddressbook(fromAddressbook, toAddressbook,
- lambda x: x.component())
+ yield _migrateAddressbook(fromAddressbook, toAddressbook,
+ lambda x: x.component())
yield self.assertAddressbooksSimilar(fromAddressbook, toAddressbook)
@@ -165,7 +163,7 @@
toHome = yield self.transactionUnderTest().addressbookHomeWithUID(
"new-home", create=True
)
- migrateHome(fromHome, toHome, lambda x: x.component())
+ yield migrateHome(fromHome, toHome, lambda x: x.component())
toAddressbooks = yield toHome.addressbooks()
self.assertEquals(set([c.name() for c in toAddressbooks]),
set([k for k in self.requirements['home1'].keys()
@@ -173,7 +171,7 @@
fromAddressbooks = yield fromHome.addressbooks()
for c in fromAddressbooks:
self.assertPropertiesSimilar(
- c, toHome.addressbookWithName(c.name()),
+ c, (yield toHome.addressbookWithName(c.name())),
builtinProperties
)
self.assertPropertiesSimilar(fromHome, toHome, builtinProperties)
@@ -192,58 +190,6 @@
@inlineCallbacks
- def test_homeProvisioningConcurrency(self):
- """
- Test that two concurrent attempts to provision an addressbook home do
- not cause a race-condition whereby the second commit results in a
- second INSERT that violates a unique constraint. Also verify that,
- whilst the two provisioning attempts are happening and doing various
- lock operations, that we do not block other reads of the table.
- """
-
- addressbookStore1 = yield buildStore(self, self.notifierFactory)
- addressbookStore2 = yield buildStore(self, self.notifierFactory)
- addressbookStore3 = yield buildStore(self, self.notifierFactory)
-
- txn1 = addressbookStore1.newTransaction()
- txn2 = addressbookStore2.newTransaction()
- txn3 = addressbookStore3.newTransaction()
-
- # Provision one home now - we will use this to later verify we can do reads of
- # existing data in the table
- home_uid2 = txn3.homeWithUID(EADDRESSBOOKTYPE, "uid2", create=True)
- self.assertNotEqual(home_uid2, None)
- yield txn3.commit()
-
- home_uid1_1 = txn1.homeWithUID(EADDRESSBOOKTYPE, "uid1", create=True)
-
- def _defer_home_uid1_2():
- home_uid1_2 = txn2.homeWithUID(EADDRESSBOOKTYPE, "uid1", create=True)
- txn2.commit() # FIXME: CONCURRENT
- return home_uid1_2
- d1 = deferToThread(_defer_home_uid1_2)
-
- def _pause_home_uid1_1():
- time.sleep(1)
- txn1.commit() # FIXME: CONCURRENT
- d2 = deferToThread(_pause_home_uid1_1)
-
- # Verify that we can still get to the existing home - i.e. the lock
- # on the table allows concurrent reads
- txn4 = addressbookStore3.newTransaction()
- home_uid2 = txn4.homeWithUID(EADDRESSBOOKTYPE, "uid2", create=True)
- self.assertNotEqual(home_uid2, None)
- yield txn4.commit()
-
- # Now do the concurrent provision attempt
- yield d2
- home_uid1_2 = yield d1
-
- self.assertNotEqual(home_uid1_1, None)
- self.assertNotEqual(home_uid1_2, None)
-
-
- @inlineCallbacks
def test_putConcurrency(self):
"""
Test that two concurrent attempts to PUT different address book object resources to the
@@ -255,21 +201,22 @@
# Provision the home now
txn = addressbookStore1.newTransaction()
- home = txn.homeWithUID(EADDRESSBOOKTYPE, "uid1", create=True)
+ home = yield txn.homeWithUID(EADDRESSBOOKTYPE, "uid1", create=True)
self.assertNotEqual(home, None)
yield txn.commit()
txn1 = addressbookStore1.newTransaction()
txn2 = addressbookStore2.newTransaction()
- home1 = txn1.homeWithUID(EADDRESSBOOKTYPE, "uid1", create=True)
- home2 = txn2.homeWithUID(EADDRESSBOOKTYPE, "uid1", create=True)
-
- adbk1 = home1.addressbookWithName("addressbook")
- adbk2 = home2.addressbookWithName("addressbook")
+ home1 = yield txn1.homeWithUID(EADDRESSBOOKTYPE, "uid1", create=True)
+ home2 = yield txn2.homeWithUID(EADDRESSBOOKTYPE, "uid1", create=True)
+ adbk1 = yield home1.addressbookWithName("addressbook")
+ adbk2 = yield home2.addressbookWithName("addressbook")
+
+ @inlineCallbacks
def _defer1():
- adbk1.createObjectResourceWithName("1.vcf", VCard.fromString(
+ yield adbk1.createObjectResourceWithName("1.vcf", VCard.fromString(
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default1;;;
@@ -283,11 +230,12 @@
END:VCARD
""".replace("\n", "\r\n")
))
- txn1.commit() # FIXME: CONCURRENT
- d1 = deferToThread(_defer1)
-
+ yield txn1.commit() # FIXME: CONCURRENT
+ d1 = _defer1()
+
+ @inlineCallbacks
def _defer2():
- adbk2.createObjectResourceWithName("2.vcf", VCard.fromString(
+ yield adbk2.createObjectResourceWithName("2.vcf", VCard.fromString(
"""BEGIN:VCARD
VERSION:3.0
N:Thompson;Default2;;;
@@ -301,8 +249,8 @@
END:VCARD
""".replace("\n", "\r\n")
))
- txn2.commit() # FIXME: CONCURRENT
- d2 = deferToThread(_defer2)
+ yield txn2.commit() # FIXME: CONCURRENT
+ d2 = _defer2()
yield d1
yield d2
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/util.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/util.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/carddav/datastore/util.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -29,6 +29,7 @@
from twext.python.log import Logger
log = Logger()
+
def validateAddressBookComponent(addressbookObject, vcard, component, inserting):
"""
Validate an addressbook component for a particular addressbook.
@@ -62,6 +63,7 @@
raise InvalidObjectResourceError(e)
+
@inlineCallbacks
def _migrateAddressbook(inAddressbook, outAddressbook, getComponent):
"""
@@ -78,9 +80,9 @@
for addressbookObject in inObjects:
try:
- outAddressbook.createAddressBookObjectWithName(
+ yield outAddressbook.createAddressBookObjectWithName(
addressbookObject.name(),
- addressbookObject.component()) # XXX WRONG SHOULD CALL getComponent
+ (yield addressbookObject.component())) # XXX WRONG SHOULD CALL getComponent
# Only the owner's properties are migrated, since previous releases of
# addressbook server didn't have per-user properties.
@@ -99,12 +101,12 @@
@inlineCallbacks
def migrateHome(inHome, outHome, getComponent=lambda x:x.component()):
- outHome.removeAddressBookWithName("addressbook")
+ yield outHome.removeAddressBookWithName("addressbook")
outHome.properties().update(inHome.properties())
- inAddressbooks = inHome.addressbooks()
+ inAddressbooks = yield inHome.addressbooks()
for addressbook in inAddressbooks:
name = addressbook.name()
- outHome.createAddressBookWithName(name)
+ yield outHome.createAddressBookWithName(name)
outAddressbook = yield outHome.addressbookWithName(name)
try:
yield _migrateAddressbook(addressbook, outAddressbook, getComponent)
@@ -112,3 +114,4 @@
log.error(" Failed to migrate address book: %s/%s" % (inHome.name(), name,))
+
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -25,6 +25,10 @@
"CommonHome",
]
+import sys
+
+from Queue import Queue
+
from twext.python.log import Logger, LoggingMixIn
from twext.web2.dav.element.rfc2518 import ResourceType
from twext.web2.http_headers import MimeType
@@ -34,6 +38,10 @@
from twisted.python.modules import getModule
from twisted.python.util import FancyEqMixin
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue, Deferred
+from twisted.python.failure import Failure
+
from twistedcaldav.customxml import NotificationType
from twistedcaldav.dateops import datetimeMktime
@@ -108,7 +116,7 @@
def newTransaction(self, label="unlabeled", migrating=False):
return CommonStoreTransaction(
self,
- self.connectionFactory(),
+ self.connectionFactory,
self.enableCalendars,
self.enableAddressBooks,
self.notifierFactory,
@@ -116,18 +124,104 @@
migrating,
)
+
+_DONE = object()
+
+_STATE_STOPPED = 'STOPPED'
+_STATE_RUNNING = 'RUNNING'
+_STATE_STOPPING = 'STOPPING'
+
+class ThreadHolder(object):
+ """
+ A queue which will hold a reactor threadpool thread open until all of the
+ work in that queue is done.
+ """
+
+ def __init__(self, reactor):
+ self._reactor = reactor
+ self._state = _STATE_STOPPED
+ self._stopper = None
+ self._q = None
+
+
+ def _run(self):
+ """
+ Worker function which runs in a non-reactor thread.
+ """
+ while True:
+ work = self._q.get()
+ if work is _DONE:
+ def finishStopping():
+ self._state = _STATE_STOPPED
+ self._q = None
+ s = self._stopper
+ self._stopper = None
+ s.callback(None)
+ self._reactor.callFromThread(finishStopping)
+ return
+ self._oneWorkUnit(*work)
+
+
+ def _oneWorkUnit(self, deferred, instruction):
+ try:
+ result = instruction()
+ except:
+ etype, evalue, etb = sys.exc_info()
+ def relayFailure():
+ f = Failure(evalue, etype, etb)
+ deferred.errback(f)
+ self._reactor.callFromThread(relayFailure)
+ else:
+ self._reactor.callFromThread(deferred.callback, result)
+
+
+ def submit(self, work):
+ """
+ Submit some work to be run.
+
+ @param work: a 0-argument callable, which will be run in a thread.
+
+ @return: L{Deferred} that fires with the result of L{work}
+ """
+ d = Deferred()
+ self._q.put((d, work))
+ return d
+
+
+ def start(self):
+ """
+ Start this thing, if it's stopped.
+ """
+ if self._state != _STATE_STOPPED:
+ raise RuntimeError("Not stopped.")
+ self._state = _STATE_RUNNING
+ self._q = Queue(0)
+ self._reactor.callInThread(self._run)
+
+
+ def stop(self):
+ """
+ Stop this thing and release its thread, if it's running.
+ """
+ if self._state != _STATE_RUNNING:
+ raise RuntimeError("Not running.")
+ s = self._stopper = Deferred()
+ self._state = _STATE_STOPPING
+ self._q.put(_DONE)
+ return s
+
+
+
class CommonStoreTransaction(object):
"""
Transaction implementation for SQL database.
"""
-
_homeClass = {}
- def __init__(self, store, connection, enableCalendars, enableAddressBooks, notifierFactory, label, migrating=False):
-
+ def __init__(self, store, connectionFactory,
+ enableCalendars, enableAddressBooks,
+ notifierFactory, label, migrating=False):
self._store = store
- self._connection = connection
- self._cursor = connection.cursor()
self._completed = False
self._calendarHomes = {}
self._addressbookHomes = {}
@@ -148,7 +242,20 @@
from txdav.carddav.datastore.sql import AddressBookHome
CommonStoreTransaction._homeClass[ECALENDARTYPE] = CalendarHome
CommonStoreTransaction._homeClass[EADDRESSBOOKTYPE] = AddressBookHome
+ self._holder = ThreadHolder(reactor)
+ self._holder.start()
+ def initCursor():
+ # support threadlevel=1; we can't necessarily cursor() in a
+ # different thread than we do transactions in.
+ # FIXME: may need to be pooling ThreadHolders along with
+ # connections, if threadlevel=1 requires connect() be called in the
+ # same thread as cursor() et. al.
+ self._connection = connectionFactory()
+ self._cursor = self._connection.cursor()
+ self._holder.submit(initCursor)
+
+
def store(self):
return self._store
@@ -157,8 +264,7 @@
return 'PG-TXN<%s>' % (self._label,)
- def execSQL(self, sql, args=[], raiseOnZeroRowCount=None):
- # print 'EXECUTE %s: %s' % (self._label, sql)
+ def _reallyExecSQL(self, sql, args=[], raiseOnZeroRowCount=None):
self._cursor.execute(sql, args)
if raiseOnZeroRowCount is not None and self._cursor.rowcount == 0:
raise raiseOnZeroRowCount()
@@ -168,75 +274,94 @@
return None
+ def execSQL(self, *args, **kw):
+ def reportResult(results):
+ sys.stdout.write("\n".join([
+ "",
+ "SQL: %r %r" % (args, kw),
+ "Results: %r" % (results,),
+ "",
+ ]))
+ return results
+ return self._holder.submit(
+ lambda : self._reallyExecSQL(*args, **kw)
+ )#.addBoth(reportResult)
+
+
def __del__(self):
if not self._completed:
- self._connection.rollback()
- self._connection.close()
+ print 'CommonStoreTransaction.__del__: OK'
+ self.abort()
@memoized('uid', '_calendarHomes')
def calendarHomeWithUID(self, uid, create=False):
return self.homeWithUID(ECALENDARTYPE, uid, create=create)
+
@memoized('uid', '_addressbookHomes')
def addressbookHomeWithUID(self, uid, create=False):
return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
+
+ @inlineCallbacks
def homeWithUID(self, storeType, uid, create=False):
-
if storeType == ECALENDARTYPE:
homeTable = CALENDAR_HOME_TABLE
elif storeType == EADDRESSBOOKTYPE:
homeTable = ADDRESSBOOK_HOME_TABLE
+ else:
+ raise RuntimeError("Unknown home type.")
- data = self.execSQL(
- "select %(column_RESOURCE_ID)s from %(name)s where %(column_OWNER_UID)s = %%s" % homeTable,
+ data = yield self.execSQL(
+ "select %(column_RESOURCE_ID)s from %(name)s"
+ " where %(column_OWNER_UID)s = %%s" % homeTable,
[uid]
)
if not data:
if not create:
- return None
-
+ returnValue(None)
# Need to lock to prevent race condition
# FIXME: this is an entire table lock - ideally we want a row lock
- # but the row does not exist yet. However, the "exclusive" mode does
- # allow concurrent reads so the only thing we block is other attempts
- # to provision a home, which is not too bad
- self.execSQL(
+ # but the row does not exist yet. However, the "exclusive" mode
+ # does allow concurrent reads so the only thing we block is other
+ # attempts to provision a home, which is not too bad
+ yield self.execSQL(
"lock %(name)s in exclusive mode" % homeTable,
)
-
# Now test again
- data = self.execSQL(
- "select %(column_RESOURCE_ID)s from %(name)s where %(column_OWNER_UID)s = %%s" % homeTable,
+ data = yield self.execSQL(
+ "select %(column_RESOURCE_ID)s from %(name)s"
+ " where %(column_OWNER_UID)s = %%s" % homeTable,
[uid]
)
-
if not data:
- self.execSQL(
+ yield self.execSQL(
"insert into %(name)s (%(column_OWNER_UID)s) values (%%s)" % homeTable,
[uid]
)
- home = self.homeWithUID(storeType, uid)
- home.createdHome()
- return home
+ home = yield self.homeWithUID(storeType, uid)
+ yield home.createdHome()
+ returnValue(home)
resid = data[0][0]
-
if self._notifierFactory:
- notifier = self._notifierFactory.newNotifier(id=uid,
- prefix=NotifierPrefixes[storeType])
+ notifier = self._notifierFactory.newNotifier(
+ id=uid, prefix=NotifierPrefixes[storeType]
+ )
else:
notifier = None
+ homeObject = self._homeClass[storeType](self, uid, resid, notifier)
+ yield homeObject._loadPropertyStore()
+ returnValue(homeObject)
- return self._homeClass[storeType](self, uid, resid, notifier)
-
@memoized('uid', '_notificationHomes')
+ @inlineCallbacks
def notificationsWithUID(self, uid):
"""
Implement notificationsWithUID.
"""
- rows = self.execSQL(
+ rows = yield self.execSQL(
"""
select %(column_RESOURCE_ID)s from %(name)s where
%(column_OWNER_UID)s = %%s
@@ -246,34 +371,43 @@
resourceID = rows[0][0]
created = False
else:
- resourceID = str(self.execSQL(
+ resourceID = str((yield self.execSQL(
"insert into %(name)s (%(column_OWNER_UID)s) values (%%s) returning %(column_RESOURCE_ID)s" % NOTIFICATION_HOME_TABLE,
[uid]
- )[0][0])
+ ))[0][0])
created = True
collection = NotificationCollection(self, uid, resourceID)
+ yield collection._loadPropertyStore()
if created:
collection._initSyncToken()
- return collection
+ returnValue(collection)
+
def abort(self):
if not self._completed:
- # print 'ABORTING', self._label
+ def reallyAbort():
+ self._connection.rollback()
+ self._connection.close()
self._completed = True
- self._connection.rollback()
- self._connection.close()
+ result = self._holder.submit(reallyAbort)
+ self._holder.stop()
+ return result
else:
raise AlreadyFinishedError()
def commit(self):
if not self._completed:
- # print 'COMPLETING', self._label
self._completed = True
- self._connection.commit()
- self._connection.close()
- for operation in self._postCommitOperations:
- operation()
+ def postCommit(ignored):
+ for operation in self._postCommitOperations:
+ operation()
+ def reallyCommit():
+ self._connection.commit()
+ self._connection.close()
+ result = self._holder.submit(reallyCommit).addCallback(postCommit)
+ self._holder.stop()
+ return result
else:
raise AlreadyFinishedError()
@@ -283,8 +417,9 @@
Run things after 'commit.'
"""
self._postCommitOperations.append(operation)
- # FIXME: implement.
+
+
class CommonHome(LoggingMixIn):
_childClass = None
@@ -335,15 +470,19 @@
return self.uid()
+ @inlineCallbacks
def children(self):
"""
Retrieve children contained in this home.
"""
- names = self.listChildren()
+ x = []
+ names = yield self.listChildren()
for name in names:
- yield self.childWithName(name)
+ x.append((yield self.childWithName(name)))
+ returnValue(x)
+ @inlineCallbacks
def listChildren(self):
"""
Retrieve the names of the children in this home.
@@ -352,7 +491,7 @@
"""
# FIXME: not specified on the interface or exercised by the tests, but
# required by clients of the implementation!
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"select %(column_RESOURCE_NAME)s from %(name)s where "
"%(column_HOME_RESOURCE_ID)s = %%s "
"and %(column_BIND_MODE)s = %%s " % self._bindTable,
@@ -360,57 +499,60 @@
[self._resourceID, _BIND_MODE_OWN]
)
names = [row[0] for row in rows]
- return names
+ returnValue(names)
@memoized('name', '_children')
+ @inlineCallbacks
def childWithName(self, name):
"""
Retrieve the child with the given C{name} contained in this
home.
@param name: a string.
- @return: an L{ICalendar} or C{None} if no such child
- exists.
+ @return: an L{ICalendar} or C{None} if no such child exists.
"""
- data = self._txn.execSQL(
+ data = yield self._txn.execSQL(
"select %(column_RESOURCE_ID)s from %(name)s where "
"%(column_RESOURCE_NAME)s = %%s and %(column_HOME_RESOURCE_ID)s = %%s "
"and %(column_BIND_MODE)s = %%s" % self._bindTable,
[name, self._resourceID, _BIND_MODE_OWN]
)
if not data:
- return None
+ returnValue(None)
resourceID = data[0][0]
if self._notifier:
childID = "%s/%s" % (self.uid(), name)
notifier = self._notifier.clone(label="collection", id=childID)
else:
notifier = None
- return self._childClass(self, name, resourceID, notifier)
+ child = self._childClass(self, name, resourceID, notifier)
+ yield child._loadPropertyStore()
+ returnValue(child)
+ @inlineCallbacks
def createChildWithName(self, name):
if name.startswith("."):
raise HomeChildNameNotAllowedError(name)
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"select %(column_RESOURCE_NAME)s from %(name)s where "
"%(column_RESOURCE_NAME)s = %%s AND "
"%(column_HOME_RESOURCE_ID)s = %%s" % self._bindTable,
[name, self._resourceID]
)
if rows:
- raise HomeChildNameAlreadyExistsError()
+ raise HomeChildNameAlreadyExistsError(name)
- rows = self._txn.execSQL("select nextval('RESOURCE_ID_SEQ')")
+ rows = yield self._txn.execSQL("select nextval('RESOURCE_ID_SEQ')")
resourceID = rows[0][0]
- self._txn.execSQL(
+ yield self._txn.execSQL(
"insert into %(name)s (%(column_RESOURCE_ID)s) values "
"(%%s)" % self._childTable,
[resourceID])
- self._txn.execSQL("""
+ yield self._txn.execSQL("""
insert into %(name)s (
%(column_HOME_RESOURCE_ID)s,
%(column_RESOURCE_ID)s, %(column_RESOURCE_NAME)s, %(column_BIND_MODE)s,
@@ -421,7 +563,7 @@
_BIND_STATUS_ACCEPTED]
)
- newChild = self.childWithName(name)
+ newChild = yield self.childWithName(name)
newChild.properties()[
PropertyName.fromElement(ResourceType)
] = newChild.resourceType()
@@ -435,14 +577,14 @@
pass
+ @inlineCallbacks
def removeChildWithName(self, name):
-
- child = self.childWithName(name)
+ child = yield self.childWithName(name)
if not child:
raise NoSuchHomeChildError()
child._deletedSyncToken()
- self._txn.execSQL(
+ yield self._txn.execSQL(
"delete from %(name)s where %(column_RESOURCE_ID)s = %%s" % self._childTable,
[child._resourceID]
)
@@ -453,16 +595,19 @@
child.notifyChanged()
+ @inlineCallbacks
def syncToken(self):
- revision = self._txn.execSQL(
+ revision = yield self._txn.execSQL(
"""
select max(%(column_REVISION)s) from %(name)s
where %(column_HOME_RESOURCE_ID)s = %%s
""" % self._revisionsTable,
[self._resourceID,]
)[0][0]
- return "%s#%s" % (self._resourceID, revision)
+ returnValue("%s#%s" % (self._resourceID, revision))
+
+ @inlineCallbacks
def resourceNamesSinceToken(self, token, depth):
results = [
(
@@ -471,14 +616,14 @@
deleted
)
for path, collection, name, deleted in
- self._txn.execSQL("""
+ (yield self._txn.execSQL("""
select %(BIND:column_RESOURCE_NAME)s, %(REV:column_COLLECTION_NAME)s, %(REV:column_RESOURCE_NAME)s, %(REV:column_DELETED)s
from %(REV:name)s
left outer join %(BIND:name)s on (%(REV:name)s.%(REV:column_RESOURCE_ID)s = %(BIND:name)s.%(BIND:column_RESOURCE_ID)s)
where %(REV:column_REVISION)s > %%s and %(REV:name)s.%(REV:column_HOME_RESOURCE_ID)s = %%s
""" % self._revisionBindJoinTable,
[token, self._resourceID],
- )
+ ))
]
deleted = []
@@ -497,16 +642,22 @@
changed.sort()
deleted.sort()
- return changed, deleted,
+ returnValue((changed, deleted))
- @cached
- def properties(self):
- return PropertyStore(
+
+ @inlineCallbacks
+ def _loadPropertyStore(self):
+ props = yield PropertyStore.load(
self.uid(),
self._txn,
self._resourceID
)
+ self._propertyStore = props
+
+ def properties(self):
+ return self._propertyStore
+
# IDataStoreResource
def contentType(self):
@@ -586,16 +737,19 @@
def retrieveOldInvites(self):
return self._invites
+
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self._resourceID)
+
def name(self):
return self._name
+ @inlineCallbacks
def rename(self, name):
oldName = self._name
- self._txn.execSQL(
+ yield self._txn.execSQL(
"update %(name)s set %(column_RESOURCE_NAME)s = %%s "
"where %(column_RESOURCE_ID)s = %%s AND "
"%(column_HOME_RESOURCE_ID)s = %%s" % self._bindTable,
@@ -618,67 +772,92 @@
self.properties()._setPerUserUID(uid)
+ @inlineCallbacks
def objectResources(self):
- for name in self.listObjectResources():
- yield self.objectResourceWithName(name)
+ x = []
+ r = x.append
+ for name in (yield self.listObjectResources()):
+ r((yield self.objectResourceWithName(name)))
+ returnValue(x)
+ @inlineCallbacks
def listObjectResources(self):
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"select %(column_RESOURCE_NAME)s from %(name)s "
"where %(column_PARENT_RESOURCE_ID)s = %%s" % self._objectTable,
[self._resourceID])
- return sorted([row[0] for row in rows])
+ returnValue(sorted([row[0] for row in rows]))
@memoized('name', '_objects')
+ @inlineCallbacks
def objectResourceWithName(self, name):
- rows = self._txn.execSQL(
- "select %(column_RESOURCE_ID)s from %(name)s "
+ rows = yield self._txn.execSQL(
+ "select %(column_RESOURCE_ID)s, %(column_UID)s from %(name)s "
"where %(column_RESOURCE_NAME)s = %%s and %(column_PARENT_RESOURCE_ID)s = %%s" % self._objectTable,
[name, self._resourceID]
)
if not rows:
- return None
- resid = rows[0][0]
- return self._objectResourceClass(name, self, resid)
+ returnValue(None)
+ [resid, uid] = rows[0]
+ returnValue((yield self._makeObjectResource(name, resid, uid)))
+ @inlineCallbacks
+ def _makeObjectResource(self, name, resid, uid):
+ """
+ Create an instance of C{self._objectResourceClass}.
+ """
+ objectResource = yield self._objectResourceClass(
+ name, self, resid, uid
+ )
+ yield objectResource._loadPropertyStore()
+ returnValue(objectResource)
+
+
@memoized('uid', '_objects')
+ @inlineCallbacks
def objectResourceWithUID(self, uid):
- rows = self._txn.execSQL(
- "select %(column_RESOURCE_ID)s, %(column_RESOURCE_NAME)s from %(name)s "
- "where %(column_UID)s = %%s and %(column_PARENT_RESOURCE_ID)s = %%s" % self._objectTable,
+ rows = yield self._txn.execSQL(
+ "select %(column_RESOURCE_ID)s, %(column_RESOURCE_NAME)s "
+ "from %(name)s where %(column_UID)s = %%s "
+ "and %(column_PARENT_RESOURCE_ID)s = %%s" % self._objectTable,
[uid, self._resourceID]
)
if not rows:
- return None
+ returnValue(None)
resid = rows[0][0]
name = rows[0][1]
- return self._objectResourceClass(name, self, resid)
+ returnValue((yield self._makeObjectResource(name, resid, uid)))
+ @inlineCallbacks
def createObjectResourceWithName(self, name, component):
if name.startswith("."):
raise ObjectResourceNameNotAllowedError(name)
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"select %(column_RESOURCE_ID)s from %(name)s "
- "where %(column_RESOURCE_NAME)s = %%s and %(column_PARENT_RESOURCE_ID)s = %%s" % self._objectTable,
+ "where %(column_RESOURCE_NAME)s = %%s "
+ "and %(column_PARENT_RESOURCE_ID)s = %%s" % self._objectTable,
[name, self._resourceID]
)
if rows:
raise ObjectResourceNameAlreadyExistsError()
- objectResource = self._objectResourceClass(name, self, None)
- objectResource.setComponent(component, inserting=True)
+ objectResource = (
+ yield self._makeObjectResource(name, None, component.resourceUID())
+ )
+ yield objectResource.setComponent(component, inserting=True)
# Note: setComponent triggers a notification, so we don't need to
# call notify( ) here like we do for object removal.
+ @inlineCallbacks
def removeObjectResourceWithName(self, name):
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"delete from %(name)s "
"where %(column_RESOURCE_NAME)s = %%s and %(column_PARENT_RESOURCE_ID)s = %%s "
"returning %(column_UID)s" % self._objectTable,
@@ -693,8 +872,9 @@
self.notifyChanged()
+ @inlineCallbacks
def removeObjectResourceWithUID(self, uid):
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"delete from %(name)s "
"where %(column_UID)s = %%s and %(column_PARENT_RESOURCE_ID)s = %%s "
"returning %(column_RESOURCE_NAME)s" % self._objectTable,
@@ -709,29 +889,33 @@
self.notifyChanged()
+ @inlineCallbacks
def syncToken(self):
- revision = self._txn.execSQL(
+ revision = yield self._txn.execSQL(
"""
select %(column_REVISION)s from %(name)s
where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
""" % self._revisionsTable,
[self._resourceID,]
)[0][0]
- return "%s#%s" % (self._resourceID, revision,)
+ returnValue(("%s#%s" % (self._resourceID, revision,)))
+
def objectResourcesSinceToken(self, token):
raise NotImplementedError()
+
+ @inlineCallbacks
def resourceNamesSinceToken(self, token):
results = [
(name if name else "", deleted)
for name, deleted in
- self._txn.execSQL("""
+ (yield self._txn.execSQL("""
select %(column_RESOURCE_NAME)s, %(column_DELETED)s from %(name)s
where %(column_REVISION)s > %%s and %(column_RESOURCE_ID)s = %%s
""" % self._revisionsTable,
[token, self._resourceID],
- )
+ ))
]
results.sort(key=lambda x:x[1])
@@ -745,7 +929,7 @@
else:
changed.append(name)
- return changed, deleted,
+ returnValue((changed, deleted))
def _initSyncToken(self):
@@ -815,9 +999,11 @@
def _deleteRevision(self, name):
self._changeRevision("delete", name)
+
+ @inlineCallbacks
def _changeRevision(self, action, name):
- nextrevision = self._txn.execSQL("""
+ nextrevision = yield self._txn.execSQL("""
select nextval('%(sequence)s')
""" % self._revisionsTable
)
@@ -839,7 +1025,7 @@
)
elif action == "update":
self._txn.execSQL("""
- update %(name)s
+ ; update %(name)s
set (%(column_REVISION)s) = (%%s)
where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
""" % self._revisionsTable,
@@ -857,13 +1043,12 @@
# was deleted. In that case an entry in the REVISIONS table still exists so we have to
# detect that and do db INSERT or UPDATE as appropriate
- self._txn.execSQL("""
+ found = bool( (yield self._txn.execSQL("""
select %(column_RESOURCE_ID)s from %(name)s
where %(column_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
""" % self._revisionsTable,
[self._resourceID, name, ]
- )
- found = self._txn._cursor.rowcount != 0
+ )) )
if found:
self._txn.execSQL("""
update %(name)s
@@ -888,16 +1073,21 @@
[nextrevision, self._resourceID,]
)
- @cached
- def properties(self):
- props = PropertyStore(
+
+ @inlineCallbacks
+ def _loadPropertyStore(self):
+ props = yield PropertyStore.load(
self.ownerHome().uid(),
self._txn,
self._resourceID
)
self.initPropertyStore(props)
- return props
+ self._properties = props
+
+ def properties(self):
+ return self._properties
+
def initPropertyStore(self, props):
"""
A hook for subclasses to override in order to set up their property
@@ -966,19 +1156,38 @@
_objectTable = None
- def __init__(self, name, parent, resid):
+ def __init__(self, name, parent, resid, uid):
self._name = name
self._parentCollection = parent
self._resourceID = resid
self._objectText = None
+ self._uid = uid
+
+ @inlineCallbacks
+ def _loadPropertyStore(self):
+ props = yield PropertyStore.load(
+ self.uid(),
+ self._txn,
+ self._resourceID
+ )
+ self.initPropertyStore(props)
+ self._propertyStore = props
+
+
+ def properties(self):
+ return self._propertyStore
+
+
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self._resourceID)
+
@property
def _txn(self):
return self._parentCollection._txn
+
def setComponent(self, component, inserting=False):
raise NotImplementedError
@@ -987,23 +1196,19 @@
raise NotImplementedError
- def text(self):
- raise NotImplementedError
+ @inlineCallbacks
+ def componentType(self):
+ returnValue((yield self.component()).mainType())
def uid(self):
- raise NotImplementedError
+ return self._uid
- @cached
- def properties(self):
- props = PropertyStore(
- self.uid(),
- self._txn,
- self._resourceID
- )
- self.initPropertyStore(props)
- return props
+ def name(self):
+ return self._name
+
+
def initPropertyStore(self, props):
"""
A hook for subclasses to override in order to set up their property
@@ -1011,15 +1216,17 @@
@param props: the L{PropertyStore} from C{properties()}.
"""
- pass
+
# IDataStoreResource
def contentType(self):
raise NotImplementedError()
+
def md5(self):
return None
+
def size(self):
size = self._txn.execSQL(
"select character_length(%(column_TEXT)s) from %(name)s "
@@ -1038,6 +1245,7 @@
utc = datetime.datetime.strptime(created, "%Y-%m-%d %H:%M:%S.%f")
return datetimeMktime(utc)
+
def modified(self):
modified = self._txn.execSQL(
"select %(column_MODIFIED)s from %(name)s "
@@ -1047,6 +1255,22 @@
utc = datetime.datetime.strptime(modified, "%Y-%m-%d %H:%M:%S.%f")
return datetimeMktime(utc)
+
+ @inlineCallbacks
+ def text(self):
+ if self._objectText is None:
+ text = (yield self._txn.execSQL(
+ "select %(column_TEXT)s from %(name)s where "
+ "%(column_RESOURCE_ID)s = %%s" % self._objectTable,
+ [self._resourceID]
+ ))[0][0]
+ self._objectText = text
+ returnValue(text)
+ else:
+ returnValue(self._objectText)
+
+
+
class NotificationCollection(LoggingMixIn, FancyEqMixin):
implements(INotificationCollection)
@@ -1064,6 +1288,15 @@
self._notifications = {}
+ @inlineCallbacks
+ def _loadPropertyStore(self):
+ self._propertyStore = yield PropertyStore.load(
+ self._uid,
+ self._txn,
+ self._resourceID
+ )
+
+
def resourceType(self):
return ResourceType.notification #@UndefinedVariable
@@ -1083,12 +1316,14 @@
for name in self.listNotificationObjects():
yield self.notificationObjectWithName(name)
+
+ @inlineCallbacks
def listNotificationObjects(self):
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"select (NOTIFICATION_UID) from NOTIFICATION "
"where NOTIFICATION_HOME_RESOURCE_ID = %s",
[self._resourceID])
- return sorted(["%s.xml" % row[0] for row in rows])
+ returnValue(sorted(["%s.xml" % row[0] for row in rows]))
def _nameToUID(self, name):
"""
@@ -1101,32 +1336,38 @@
def notificationObjectWithName(self, name):
return self.notificationObjectWithUID(self._nameToUID(name))
+
@memoized('uid', '_notifications')
+ @inlineCallbacks
def notificationObjectWithUID(self, uid):
- rows = self._txn.execSQL(
+ rows = (yield self._txn.execSQL(
"select RESOURCE_ID from NOTIFICATION "
"where NOTIFICATION_UID = %s and NOTIFICATION_HOME_RESOURCE_ID = %s",
- [uid, self._resourceID])
+ [uid, self._resourceID]))
if rows:
resourceID = rows[0][0]
- return NotificationObject(self, resourceID)
+ no = NotificationObject(self, resourceID)
+ yield no._loadPropertyStore()
+ returnValue(no)
else:
- return None
+ returnValue(None)
+ @inlineCallbacks
def writeNotificationObject(self, uid, xmltype, xmldata):
inserting = False
- notificationObject = self.notificationObjectWithUID(uid)
+ notificationObject = yield self.notificationObjectWithUID(uid)
if notificationObject is None:
notificationObject = NotificationObject(self, None)
inserting = True
- notificationObject.setData(uid, xmltype, xmldata, inserting=inserting)
+ yield notificationObject.setData(uid, xmltype, xmldata, inserting=inserting)
if inserting:
self._insertRevision("%s.xml" % (uid,))
else:
self._updateRevision("%s.xml" % (uid,))
+
def removeNotificationObjectWithName(self, name):
self.removeNotificationObjectWithUID(self._nameToUID(name))
@@ -1150,16 +1391,19 @@
[self._resourceID,]
)
+
+ @inlineCallbacks
def syncToken(self):
- revision = self._txn.execSQL(
+ revision = yield self._txn.execSQL(
"""
select %(column_REVISION)s from %(name)s
where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s is null
""" % self._revisionsTable,
[self._resourceID,]
)[0][0]
- return "%s#%s" % (self._resourceID, revision,)
+ returnValue("%s#%s" % (self._resourceID, revision,))
+
def objectResourcesSinceToken(self, token):
raise NotImplementedError()
@@ -1171,16 +1415,17 @@
return (changed, removed, token)
+ @inlineCallbacks
def resourceNamesSinceToken(self, token):
results = [
(name if name else "", deleted)
for name, deleted in
- self._txn.execSQL("""
+ (yield self._txn.execSQL("""
select %(column_RESOURCE_NAME)s, %(column_DELETED)s from %(name)s
where %(column_REVISION)s > %%s and %(column_HOME_RESOURCE_ID)s = %%s
""" % self._revisionsTable,
[token, self._resourceID],
- )
+ ))
]
results.sort(key=lambda x:x[1])
@@ -1194,8 +1439,9 @@
else:
changed.append(name)
- return changed, deleted,
+ returnValue((changed, deleted))
+
def _updateSyncToken(self):
self._txn.execSQL("""
@@ -1215,9 +1461,11 @@
def _deleteRevision(self, name):
self._changeRevision("delete", name)
+
+ @inlineCallbacks
def _changeRevision(self, action, name):
- nextrevision = self._txn.execSQL("""
+ nextrevision = yield self._txn.execSQL("""
select nextval('%(sequence)s')
""" % self._revisionsTable
)
@@ -1257,13 +1505,12 @@
# was deleted. In that case an entry in the REVISIONS table still exists so we have to
# detect that and do db INSERT or UPDATE as appropriate
- self._txn.execSQL("""
+ found = bool( (yield self._txn.execSQL("""
select %(column_HOME_RESOURCE_ID)s from %(name)s
where %(column_HOME_RESOURCE_ID)s = %%s and %(column_RESOURCE_NAME)s = %%s
""" % self._revisionsTable,
[self._resourceID, name, ]
- )
- found = self._txn._cursor.rowcount != 0
+ )))
if found:
self._txn.execSQL("""
update %(name)s
@@ -1288,15 +1535,14 @@
[nextrevision, self._resourceID]
)
- @cached
+
def properties(self):
- return PropertyStore(
- self._uid,
- self._txn,
- self._resourceID
- )
+ return self._propertyStore
+
+
class NotificationObject(LoggingMixIn, FancyEqMixin):
+
implements(INotificationObject)
compareAttributes = '_resourceID _home'.split()
@@ -1323,18 +1569,20 @@
return self.uid() + ".xml"
+ @inlineCallbacks
def setData(self, uid, xmltype, xmldata, inserting=False):
xmltypeString = xmltype.toxml()
if inserting:
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"insert into NOTIFICATION (NOTIFICATION_HOME_RESOURCE_ID, NOTIFICATION_UID, XML_TYPE, XML_DATA) "
"values (%s, %s, %s, %s) returning RESOURCE_ID",
[self._home._resourceID, uid, xmltypeString, xmldata]
)
self._resourceID = rows[0][0]
+ yield self._loadPropertyStore()
else:
- self._txn.execSQL(
+ yield self._txn.execSQL(
"update NOTIFICATION set XML_TYPE = %s, XML_DATA = %s "
"where NOTIFICATION_HOME_RESOURCE_ID = %s and NOTIFICATION_UID = %s",
[xmltypeString, xmldata, self._home._resourceID, uid])
@@ -1342,13 +1590,14 @@
self.properties()[PropertyName.fromElement(NotificationType)] = NotificationType(xmltype)
+ @inlineCallbacks
def _fieldQuery(self, field):
- data = self._txn.execSQL(
+ data = yield self._txn.execSQL(
"select " + field + " from NOTIFICATION "
"where RESOURCE_ID = %s",
[self._resourceID]
)
- return data[0][0]
+ returnValue(data[0][0])
def xmldata(self):
@@ -1359,16 +1608,20 @@
return self._fieldQuery("NOTIFICATION_UID")
- @cached
def properties(self):
- props = PropertyStore(
+ return self._propertyStore
+
+
+ @inlineCallbacks
+ def _loadPropertyStore(self):
+ self._propertyStore = yield PropertyStore.load(
self._home.uid(),
self._txn,
self._resourceID
)
- self.initPropertyStore(props)
- return props
+ self.initPropertyStore(self._propertyStore)
+
def initPropertyStore(self, props):
# Setup peruser special properties
props.setSpecialProperties(
@@ -1390,28 +1643,35 @@
return hashlib.md5(self.xmldata()).hexdigest()
+ @inlineCallbacks
def size(self):
- size = self._txn.execSQL(
+ size = (yield self._txn.execSQL(
"select character_length(XML_DATA) from NOTIFICATION "
"where RESOURCE_ID = %s",
[self._resourceID]
- )[0][0]
- return size
+ ))[0][0]
+ returnValue(size)
+ @inlineCallbacks
def created(self):
- created = self._txn.execSQL(
+ created = (yield self._txn.execSQL(
"select CREATED from NOTIFICATION "
"where RESOURCE_ID = %s",
[self._resourceID]
- )[0][0]
+ ))[0][0]
utc = datetime.datetime.strptime(created, "%Y-%m-%d %H:%M:%S.%f")
- return datetimeMktime(utc)
+ returnValue(datetimeMktime(utc))
+
+ @inlineCallbacks
def modified(self):
- modified = self._txn.execSQL(
+ modified = (yield self._txn.execSQL(
"select MODIFIED from NOTIFICATION "
"where RESOURCE_ID = %s", [self._resourceID]
- )[0][0]
+ ))[0][0]
utc = datetime.datetime.strptime(modified, "%Y-%m-%d %H:%M:%S.%f")
- return datetimeMktime(utc)
+ returnValue(datetimeMktime(utc))
+
+
+
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql_legacy.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql_legacy.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/sql_legacy.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -126,8 +126,11 @@
"No-op, because the index implicitly always exists in the database."
pass
+
+ @inlineCallbacks
def allRecords(self):
- for row in self._txn.execSQL(
+ values = []
+ for row in (yield self._txn.execSQL(
"""
select
INVITE.INVITE_UID,
@@ -148,11 +151,14 @@
INVITE.NAME asc
""" % self._combinedTable,
[self._collection._resourceID]
- ):
- yield self._makeInvite(row)
+ )):
+ values.append(self._makeInvite(row))
+ returnValue(values)
+
+ @inlineCallbacks
def recordForUserID(self, userid):
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"""
select
INVITE.INVITE_UID,
@@ -171,7 +177,7 @@
""" % self._combinedTable,
[userid]
)
- return self._makeInvite(rows[0]) if rows else None
+ returnValue(self._makeInvite(rows[0]) if rows else None)
@inlineCallbacks
@@ -181,8 +187,9 @@
returnValue(record)
+ @inlineCallbacks
def recordForInviteUID(self, inviteUID):
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"""
select
INVITE.INVITE_UID,
@@ -201,8 +208,9 @@
""" % self._combinedTable,
[inviteUID]
)
- return self._makeInvite(rows[0]) if rows else None
+ returnValue(self._makeInvite(rows[0]) if rows else None)
+
def _makeInvite(self, row):
[inviteuid, common_name, userid, ownerUID,
bindMode, bindStatus, summary] = row
@@ -224,6 +232,8 @@
access, state, summary
)
+
+ @inlineCallbacks
def addOrUpdateRecord(self, record):
bindMode = {'read-only': _BIND_MODE_READ,
'read-write': _BIND_MODE_WRITE}[record.access]
@@ -238,7 +248,7 @@
# (and may contain a trailing slash).
principalUID = record.principalURL.split("/")[3]
shareeHome = self._getHomeWithUID(principalUID)
- rows = self._txn.execSQL(
+ rows = yield self._txn.execSQL(
"select RESOURCE_ID, HOME_RESOURCE_ID from INVITE where RECIPIENT_ADDRESS = %s",
[record.userid]
)
@@ -394,12 +404,14 @@
pass
+ @inlineCallbacks
def allRecords(self):
# This should have been a smart join that got all these columns at
# once, but let's not bother to fix it, since the actual query we
# _want_ to do (just look for binds in a particular homes) is
# much simpler anyway; we should just do that.
- shareRows = self._txn.execSQL(
+ all = []
+ shareRows = yield self._txn.execSQL(
"""
select %(column_RESOURCE_ID)s, %(column_RESOURCE_NAME)s, %(column_BIND_MODE)s, %(column_MESSAGE)s
from %(name)s
@@ -411,7 +423,7 @@
)
for resourceID, resourceName, bindMode, summary in shareRows:
if bindMode != _BIND_MODE_DIRECT:
- [[shareuid]] = self._txn.execSQL(
+ [[shareuid]] = yield self._txn.execSQL(
"""
select INVITE_UID
from INVITE
@@ -420,7 +432,7 @@
[resourceID, self._home._resourceID]
)
sharetype = 'I'
- [[ownerHomeID, ownerResourceName]] = self._txn.execSQL(
+ [[ownerHomeID, ownerResourceName]] = yield self._txn.execSQL(
"""
select %(column_HOME_RESOURCE_ID)s, %(column_RESOURCE_NAME)s
from %(name)s
@@ -429,7 +441,7 @@
""" % self._bindTable,
[resourceID, _BIND_MODE_OWN]
)
- [[ownerUID]] = self._txn.execSQL(
+ [[ownerUID]] = yield self._txn.execSQL(
"""
select %(column_OWNER_UID)s from %(name)s
where %(column_RESOURCE_ID)s = %%s
@@ -443,10 +455,10 @@
record = SharedCollectionRecord(
shareuid, sharetype, hosturl, localname, summary
)
- yield record
+ all.append(record)
else:
sharetype = 'D'
- [[ownerHomeID, ownerResourceName]] = self._txn.execSQL(
+ [[ownerHomeID, ownerResourceName]] = yield self._txn.execSQL(
"""
select %(column_HOME_RESOURCE_ID)s, %(column_RESOURCE_NAME)s
from %(name)s
@@ -455,7 +467,7 @@
""" % self._bindTable,
[resourceID, _BIND_MODE_OWN]
)
- [[ownerUID]] = self._txn.execSQL(
+ [[ownerUID]] = yield self._txn.execSQL(
"""
select %(column_OWNER_UID)s from %(name)s
where %(column_RESOURCE_ID)s = %%s
@@ -470,14 +482,16 @@
record = SharedCollectionRecord(
synthesisedUID, sharetype, hosturl, localname, summary
)
- yield record
-
+ all.append(record)
+ returnValue(all)
+
+ @inlineCallbacks
def _search(self, **kw):
[[key, value]] = kw.items()
- for record in self.allRecords():
+ for record in (yield self.allRecords()):
if getattr(record, key) == value:
- return record
+ returnValue((record))
def recordForShareUID(self, shareUID):
@@ -494,7 +508,7 @@
collectionResourceID = ownerCollection._resourceID
if record.sharetype == 'I':
-
+
# There needs to be a bind already, one that corresponds to the
# invitation. The invitation's UID is the same as the share UID. I
# just need to update its 'localname', i.e.
@@ -892,25 +906,29 @@
returnValue(obj.name())
+ @inlineCallbacks
def notExpandedBeyond(self, minDate):
"""
Gives all resources which have not been expanded beyond a given date
in the database. (Unused; see above L{postgresqlgenerator}.
"""
- return [row[0] for row in self._txn.execSQL(
+ returnValue([row[0] for row in (yield self._txn.execSQL(
"select RESOURCE_NAME from CALENDAR_OBJECT "
"where RECURRANCE_MAX < %s and CALENDAR_RESOURCE_ID = %s",
[normalizeForIndex(minDate), self.calendar._resourceID]
- )]
+ ))])
+ @inlineCallbacks
def reExpandResource(self, name, expand_until):
"""
Given a resource name, remove it from the database and re-add it
with a longer expansion.
"""
- obj = self.calendar.calendarObjectWithName(name)
- obj.updateDatabase(obj.component(), expand_until=expand_until, reCreate=True)
+ obj = yield self.calendar.calendarObjectWithName(name)
+ yield obj.updateDatabase(
+ obj.component(), expand_until=expand_until, reCreate=True
+ )
@inlineCallbacks
@@ -921,7 +939,7 @@
# Actually expand recurrence max
for name in names:
self.log_info("Search falls outside range of index for %s %s" % (name, minDate))
- self.reExpandResource(name, minDate)
+ yield self.reExpandResource(name, minDate)
@inlineCallbacks
@@ -1027,14 +1045,15 @@
set((yield self.calendar.listCalendarObjects())))))
+ @inlineCallbacks
def resourceExists(self, name):
- return bool(
- self._txn.execSQL(
+ returnValue((bool(
+ (yield self._txn.execSQL(
"select RESOURCE_NAME from CALENDAR_OBJECT where "
"RESOURCE_NAME = %s and CALENDAR_RESOURCE_ID = %s",
[name, self.calendar._resourceID]
- )
- )
+ ))
+ )))
@@ -1171,6 +1190,8 @@
return qualifiers is not None
+
+ @inlineCallbacks
def search(self, filter):
"""
Finds resources matching the given qualifiers.
@@ -1187,20 +1208,20 @@
else:
qualifiers = None
if qualifiers is not None:
- rowiter = self._txn.execSQL(
+ rowiter = yield self._txn.execSQL(
"select DISTINCT ADDRESSBOOK_OBJECT.RESOURCE_NAME, ADDRESSBOOK_OBJECT.VCARD_UID" +
qualifiers[0],
qualifiers[1]
)
else:
- rowiter = self._txn.execSQL(
+ rowiter = yield self._txn.execSQL(
"select RESOURCE_NAME, VCARD_UID from ADDRESSBOOK_OBJECT where ADDRESSBOOK_RESOURCE_ID = %s",
[self.addressbook._resourceID, ],
)
- for row in rowiter:
- yield row
+ returnValue(list(rowiter))
+
def indexedSearch(self, filter, useruid='', fbtype=False):
"""
Always raise L{IndexedSearchException}, since these indexes are not
@@ -1223,11 +1244,12 @@
set((yield self.addressbook.listAddressbookObjects())))))
+ @inlineCallbacks
def resourceExists(self, name):
- return bool(
- self._txn.execSQL(
+ returnValue(bool(
+ (yield self._txn.execSQL(
"select RESOURCE_NAME from ADDRESSBOOK_OBJECT where "
"RESOURCE_NAME = %s and ADDRESSBOOK_RESOURCE_ID = %s",
[name, self.addressbook._resourceID]
- )
- )
+ ))
+ ))
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/test/test_util.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/test/test_util.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/test/test_util.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -82,7 +82,9 @@
self.addCleanup(txn.commit)
for uid in CommonTests.requirements:
if CommonTests.requirements[uid] is not None:
- self.assertNotIdentical(None, (yield txn.calendarHomeWithUID(uid)))
+ self.assertNotIdentical(
+ None, (yield txn.calendarHomeWithUID(uid))
+ )
# Un-migrated data should be preserved.
self.assertEquals(self.filesPath.child("calendars-migrated").child(
"__uids__").child("ho").child("me").child("home1").child(
@@ -117,6 +119,7 @@
L{UpgradeToDatabaseService.startService} upgrades calendar attachments
as well.
"""
+
txn = self.fileStore.newTransaction()
committed = []
def maybeCommit():
@@ -124,28 +127,30 @@
committed.append(True)
return txn.commit()
self.addCleanup(maybeCommit)
+
@inlineCallbacks
def getSampleObj():
home = (yield txn.calendarHomeWithUID("home1"))
calendar = (yield home.calendarWithName("calendar_1"))
object = (yield calendar.calendarObjectWithName("1.ics"))
returnValue(object)
+
inObject = yield getSampleObj()
someAttachmentName = "some-attachment"
someAttachmentType = MimeType.fromString("application/x-custom-type")
- transport = inObject.createAttachmentWithName(
+ transport = yield inObject.createAttachmentWithName(
someAttachmentName, someAttachmentType
)
someAttachmentData = "Here is some data for your attachment, enjoy."
transport.write(someAttachmentData)
transport.loseConnection()
- maybeCommit()
+ yield maybeCommit()
self.topService.startService()
yield self.subStarted
committed = []
txn = self.sqlStore.newTransaction()
outObject = yield getSampleObj()
- outAttachment = outObject.attachmentWithName(someAttachmentName)
+ outAttachment = yield outObject.attachmentWithName(someAttachmentName)
allDone = Deferred()
class SimpleProto(Protocol):
data = ''
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/test/util.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/test/util.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -171,14 +171,16 @@
# We don't want the default calendar or inbox to appear unless it's
# explicitly listed.
try:
- home.removeCalendarWithName("calendar")
- home.removeCalendarWithName("inbox")
+ yield home.removeCalendarWithName("calendar")
+ yield home.removeCalendarWithName("inbox")
except NoSuchHomeChildError:
pass
for calendarName in calendars:
calendarObjNames = calendars[calendarName]
if calendarObjNames is not None:
- home.createCalendarWithName(calendarName)
+ # XXX should not be yielding! this SQL will be executed
+ # first!
+ yield home.createCalendarWithName(calendarName)
calendar = yield home.calendarWithName(calendarName)
for objectName in calendarObjNames:
objData = calendarObjNames[objectName]
Modified: CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/util.py
===================================================================
--- CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/util.py 2010-10-13 22:16:11 UTC (rev 6424)
+++ CalendarServer/branches/users/glyph/more-deferreds-7/txdav/common/datastore/util.py 2010-10-14 09:48:50 UTC (rev 6425)
@@ -101,7 +101,8 @@
("calendar", migrateCalendarHome,
self.fileStore.eachCalendarHome,
lambda txn: txn.calendarHomeWithUID),
- ("addressbook", migrateAddressbookHome, self.fileStore.eachAddressbookHome,
+ ("addressbook", migrateAddressbookHome,
+ self.fileStore.eachAddressbookHome,
lambda txn: txn.addressbookHomeWithUID)
]:
for fileTxn, fileHome in eachFunc():
@@ -109,7 +110,7 @@
self.log_warn("Migrating %s UID %r" % (homeType, uid))
sqlTxn = self.sqlStore.newTransaction(migrating=True)
homeGetter = destFunc(sqlTxn)
- if homeGetter(uid, create=False) is not None:
+ if (yield homeGetter(uid, create=False)) is not None:
self.log_warn(
"%s home %r already existed not migrating" % (
homeType, uid))
@@ -117,6 +118,8 @@
yield fileTxn.commit()
continue
sqlHome = yield homeGetter(uid, create=True)
+ if sqlHome is None:
+ raise RuntimeError("THIS SHOULD NOT BE POSSIBLE.")
yield migrateFunc(fileHome, sqlHome)
yield fileTxn.commit()
yield sqlTxn.commit()
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20101014/1b1478d5/attachment-0001.html>
More information about the calendarserver-changes
mailing list