[CalendarServer-changes] [5519] CalendarServer/branches/users/wsanchez/transations/txcaldav/ calendarstore/file.py
source_changes at macosforge.org
source_changes at macosforge.org
Thu Apr 22 17:27:51 PDT 2010
Revision: 5519
http://trac.macosforge.org/projects/calendarserver/changeset/5519
Author: wsanchez at apple.com
Date: 2010-04-22 17:27:50 -0700 (Thu, 22 Apr 2010)
Log Message:
-----------
Work-in-progress transaction stuff
Modified Paths:
--------------
CalendarServer/branches/users/wsanchez/transations/txcaldav/calendarstore/file.py
Modified: CalendarServer/branches/users/wsanchez/transations/txcaldav/calendarstore/file.py
===================================================================
--- CalendarServer/branches/users/wsanchez/transations/txcaldav/calendarstore/file.py 2010-04-23 00:27:32 UTC (rev 5518)
+++ CalendarServer/branches/users/wsanchez/transations/txcaldav/calendarstore/file.py 2010-04-23 00:27:50 UTC (rev 5519)
@@ -36,8 +36,10 @@
from twext.python.vcomponent import VComponent
from twext.python.vcomponent import InvalidICalendarDataError
+from txdav.idav import AbortedTransactionError
from txdav.propertystore.xattr import PropertyStore
+from txcaldav.icalendarstore import ICalendarStoreTransaction
from txcaldav.icalendarstore import ICalendarStore, ICalendarHome
from txcaldav.icalendarstore import ICalendar, ICalendarObject
from txcaldav.icalendarstore import CalendarNameNotAllowedError
@@ -57,8 +59,6 @@
class CalendarStore(LoggingMixIn):
implements(ICalendarStore)
- calendarHomeClass = property(lambda _: CalendarHome)
-
def __init__(self, path):
"""
@param path: a L{FilePath}
@@ -74,31 +74,92 @@
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.path.path)
+ def newTransaction(self):
+ return Transaction(self)
+
+
+class Transaction(LoggingMixIn):
+ implements(ICalendarStoreTransaction)
+
+ def __init__(self, calendarStore):
+ self.calendarStore = calendarStore
+ self.aborted = False
+ self._operations = []
+ self._calendarHomes = {}
+
+ def addOperation(operation):
+ self._operations.append(operation)
+
+ def abort(self):
+ self.aborted = True
+
+ def commit(self):
+ assert not self.aborted
+
+ undos = []
+
+ for operation in self._operations:
+ try:
+ undo = operation()
+ if undo is not None:
+ undos.append(undo)
+ except Exception, e:
+ for undo in undos:
+ try:
+ undo()
+ except Exception, e:
+ self.log_error("Exception while undoing transaction: %s" % (e,))
+ raise
+
def calendarHomeWithUID(self, uid, create=False):
+ if (uid, self) in self._calendarHomes:
+ return self._calendarHomes[(uid, self)]
+
if uid.startswith("."):
return None
assert len(uid) >= 4
- childPath = self.path.child(uid[0:2]).child(uid[2:4]).child(uid)
+ childPath1 = self.calendarStore.path.child(uid[0:2])
+ childPath2 = childPath1.child(uid[2:4])
+ childPath3 = childPath2.child(uid)
- if not childPath.isdir():
- if create:
- childPath.makedirs()
- else:
- return None
+ def do():
+ def createDirectory(path):
+ try:
+ path.createDirectory()
+ except (IOError, OSError), e:
+ if e.errno != errno.EEXIST:
+ # Ignore, in case someone else created the
+ # directory while we were trying to as well.
+ raise
- return CalendarHome(childPath, self)
+ if not childPath3.isdir():
+ if not childPath2.isdir():
+ if not childPath1.isdir():
+ createDirectory(childPath1)
+ createDirectory(childPath2)
+ createDirectory(childPath3)
+ if create:
+ self.addOperation(do)
+ elif not childPath3.isdir():
+ return None
+ calendarHome = CalendarHome(childPath, self.calendarStore, self)
+ self._calendarHomes[(uid, self)] = calendarHome
+ return calendarHome
+
+
class CalendarHome(LoggingMixIn):
implements(ICalendarHome)
- calendarClass = property(lambda _: Calendar)
-
- def __init__(self, path, calendarStore):
+ def __init__(self, path, calendarStore, transaction):
self.path = path
self.calendarStore = calendarStore
+ self._transaction = transaction
+ self._newCalendars = {}
+ self._removedCalendars = set()
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.path)
@@ -107,13 +168,17 @@
return self.path.basename()
def calendars(self):
- return (
+ return set(self._newCalendars.itervalues()) | set(
self.calendarWithName(name)
for name in self.path.listdir()
if not name.startswith(".")
)
def calendarWithName(self, name):
+ calendar = self._newCalendars.get(name)
+ if calendar is not None:
+ return calendar
+
if name.startswith("."):
return None
@@ -129,26 +194,61 @@
childPath = self.path.child(name)
- try:
- childPath.createDirectory()
- except (IOError, OSError), e:
- if e.errno == errno.EEXIST:
- raise CalendarAlreadyExistsError(name)
- raise
+ if name not in self._removedCalendars and childPath.isdir():
+ raise CalendarAlreadyExistsError(name)
+ def do():
+ try:
+ childPath.createDirectory()
+
+ # Return undo
+ return lambda: childPath.remove()
+ except (IOError, OSError), e:
+ if e.errno == errno.EEXIST and childPath.isdir():
+ raise CalendarAlreadyExistsError(name)
+ raise
+
+ self._transaction.addOperation(do)
+ self._newCalendars[name] = Calendar(self.path.child(name), self)
+
def removeCalendarWithName(self, name):
- if name.startswith("."):
+ if name.startswith(".") or name in self._removedCalendars:
raise NoSuchCalendarError(name)
childPath = self.path.child(name)
- try:
- childPath.remove()
- except (IOError, OSError), e:
- if e.errno == errno.ENOENT:
- raise NoSuchCalendarError(name)
- raise
+ if name not in self._newCalendars and not childPath.isdir():
+ raise NoSuchCalendarError(name)
+ def do(transaction=self._transaction):
+ for i in xrange(1000):
+ trash = childPath.sibling("._del_%s_%d" % (childPath.basename(), i))
+ if not trash.exists():
+ break
+ else:
+ raise InternalDataStoreError("Unable to create trash target for calendar at %s" % (childPath,))
+
+ try:
+ childPath.moveTo(trash)
+ except (IOError, OSError), e:
+ if e.errno == errno.ENOENT:
+ raise NoSuchCalendarError(name)
+ raise
+
+ def cleanup():
+ try:
+ trash.remove()
+ except Exception, e:
+ self.log_error("Unable to delete trashed calendar at %s: %s" % (trash.fp, e))
+
+ transaction.addOperation(cleanup)
+
+ def undo():
+ trash.moveTo(childPath)
+
+ return undo
+
def properties(self):
+ raise NotImplementedError()
if not hasattr(self, "_properties"):
self._properties = PropertyStore(self.path)
return self._properties
@@ -157,11 +257,12 @@
class Calendar(LoggingMixIn):
implements(ICalendar)
- calendarObjectClass = property(lambda _: CalendarObject)
-
def __init__(self, path, calendarHome):
self.path = path
self.calendarHome = calendarHome
+ self._transaction = calendarHome.transaction
+ self._newCalendarObjects = {}
+ self._removedCalendarObjects = set()
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.path.path)
@@ -187,12 +288,18 @@
if not name.startswith(".")
)
- calendarObjects = _calendarObjects_index
+ def calendarObjects(self):
+ return set(self._newCalendarObjects.iterkeys()) | set(
+ self._calendarObjects_index()
+ )
def calendarObjectWithName(self, name):
- childPath = self.path.child(name)
- if childPath.isfile():
- return CalendarObject(childPath, self)
+ if name in self._newCalendarObjects:
+ return self._newCalendarObjects[name]
+
+ calendarObjectPath = self.path.child(name)
+ if calendarObjectPath.isfile():
+ return CalendarObject(calendarObjectPath, self)
else:
return None
@@ -203,20 +310,20 @@
if name.startswith("."):
raise CalendarObjectNameNotAllowedError(name)
- childPath = self.path.child(name)
- if childPath.exists():
+ calendarObjectPath = self.path.child(name)
+ if calendarObjectPath.exists():
raise CalendarObjectNameAlreadyExistsError(name)
- calendarObject = CalendarObject(childPath, self)
+ calendarObject = CalendarObject(calendarObjectPath, self)
calendarObject.setComponent(component)
def removeCalendarObjectWithName(self, name):
if name.startswith("."):
raise NoSuchCalendarObjectError(name)
- childPath = self.path.child(name)
- if childPath.isfile():
- childPath.remove()
+ calendarObjectPath = self.path.child(name)
+ if calendarObjectPath.isfile():
+ calendarObjectPath.remove()
else:
raise NoSuchCalendarObjectError(name)
@@ -258,6 +365,7 @@
raise NotImplementedError()
def properties(self):
+ raise NotImplementedError()
if not hasattr(self, "_properties"):
self._properties = PropertyStore(self.path)
return self._properties
@@ -371,6 +479,7 @@
return self.component().getOrganizer()
def properties(self):
+ raise NotImplementedError()
if not hasattr(self, "_properties"):
self._properties = PropertyStore(self.path)
return self._properties
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100422/684050f0/attachment-0001.html>
More information about the calendarserver-changes
mailing list