[CalendarServer-changes] [6185] CalendarServer/branches/generic-sqlstore
source_changes at macosforge.org
source_changes at macosforge.org
Wed Aug 25 18:11:34 PDT 2010
Revision: 6185
http://trac.macosforge.org/projects/calendarserver/changeset/6185
Author: cdaboo at apple.com
Date: 2010-08-25 18:11:33 -0700 (Wed, 25 Aug 2010)
Log Message:
-----------
Add txdav.base module and push stuff stuff down into that.
Modified Paths:
--------------
CalendarServer/branches/generic-sqlstore/calendarserver/tap/caldav.py
CalendarServer/branches/generic-sqlstore/calendarserver/tap/util.py
CalendarServer/branches/generic-sqlstore/twistedcaldav/schedule.py
CalendarServer/branches/generic-sqlstore/twistedcaldav/storebridge.py
CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/file.py
CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/sql.py
CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/common.py
CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/file.py
CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/sql.py
CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/test/common.py
CalendarServer/branches/generic-sqlstore/txdav/base/datastore/subpostgres.py
CalendarServer/branches/generic-sqlstore/txdav/base/datastore/test/test_subpostgres.py
CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/none.py
CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_base.py
CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_none.py
CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_xattr.py
CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/xattr.py
CalendarServer/branches/generic-sqlstore/txdav/common/datastore/file.py
CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py
CalendarServer/branches/generic-sqlstore/txdav/common/datastore/test/util.py
Added Paths:
-----------
CalendarServer/branches/generic-sqlstore/txdav/base/
CalendarServer/branches/generic-sqlstore/txdav/base/__init__.py
CalendarServer/branches/generic-sqlstore/txdav/base/datastore/
CalendarServer/branches/generic-sqlstore/txdav/base/datastore/file.py
CalendarServer/branches/generic-sqlstore/txdav/base/datastore/sql.py
CalendarServer/branches/generic-sqlstore/txdav/base/datastore/util.py
CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/
CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/sql.py
CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/base.py
CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_sql.py
Removed Paths:
-------------
CalendarServer/branches/generic-sqlstore/txdav/base/datastore/file.py
CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/base.py
CalendarServer/branches/generic-sqlstore/txdav/datastore/
CalendarServer/branches/generic-sqlstore/txdav/propertystore/
Modified: CalendarServer/branches/generic-sqlstore/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/calendarserver/tap/caldav.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/calendarserver/tap/caldav.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -89,7 +89,7 @@
from calendarserver.tools.util import checkDirectory
from txdav.common.datastore.sql import v1_schema
-from txdav.datastore.subpostgres import PostgresService
+from txdav.base.datastore.subpostgres import PostgresService
from twext.python.filepath import CachingFilePath
log = Logger()
Modified: CalendarServer/branches/generic-sqlstore/calendarserver/tap/util.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/calendarserver/tap/util.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/calendarserver/tap/util.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -70,7 +70,7 @@
from txdav.common.datastore.sql import CommonDataStore as CommonSQLDataStore
from txdav.common.datastore.file import CommonDataStore as CommonFileDataStore
from txdav.common.datastore.sql import v1_schema
-from txdav.datastore.subpostgres import PostgresService
+from txdav.base.datastore.subpostgres import PostgresService
from twext.python.filepath import CachingFilePath
Modified: CalendarServer/branches/generic-sqlstore/twistedcaldav/schedule.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/twistedcaldav/schedule.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/twistedcaldav/schedule.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -50,7 +50,7 @@
from twistedcaldav.resource import isCalendarCollectionResource
from twistedcaldav.scheduling.scheduler import CalDAVScheduler, IScheduleScheduler
-from txdav.propertystore.base import PropertyName
+from txdav.base.propertystore.base import PropertyName
def _schedulePrivilegeSet(deliver):
edited = False
Modified: CalendarServer/branches/generic-sqlstore/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/twistedcaldav/storebridge.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/twistedcaldav/storebridge.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -56,7 +56,7 @@
from twistedcaldav.vcard import Component as VCard
from txdav.common.icommondatastore import NoSuchObjectResourceError
-from txdav.propertystore.base import PropertyName
+from txdav.base.propertystore.base import PropertyName
log = Logger()
Modified: CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/file.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/file.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/file.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -34,7 +34,7 @@
from twisted.internet.interfaces import ITransport
from twisted.python.failure import Failure
-from txdav.propertystore.xattr import PropertyStore
+from txdav.base.propertystore.xattr import PropertyStore
from twext.python.vcomponent import InvalidICalendarDataError
from twext.python.vcomponent import VComponent
@@ -62,8 +62,8 @@
from txdav.common.icommondatastore import (NoSuchObjectResourceError,
InternalDataStoreError)
-from txdav.datastore.file import writeOperation, hidden, FileMetaDataMixin
-from txdav.propertystore.base import PropertyName
+from txdav.base.datastore.file import writeOperation, hidden, FileMetaDataMixin
+from txdav.base.propertystore.base import PropertyName
from zope.interface import implements
Modified: CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/sql.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/sql.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/sql.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -49,7 +49,7 @@
from txdav.common.datastore.sql_tables import CALENDAR_TABLE,\
CALENDAR_BIND_TABLE, CALENDAR_OBJECT_REVISIONS_TABLE, CALENDAR_OBJECT_TABLE,\
_ATTACHMENTS_MODE_WRITE
-from txdav.propertystore.base import PropertyName
+from txdav.base.propertystore.base import PropertyName
from vobject.icalendar import utc
Modified: CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/common.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/common.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/common.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -27,7 +27,7 @@
from twisted.internet.protocol import Protocol
from txdav.idav import IPropertyStore, IDataStore, AlreadyFinishedError
-from txdav.propertystore.base import PropertyName
+from txdav.base.propertystore.base import PropertyName
from txdav.common.icommondatastore import HomeChildNameAlreadyExistsError, \
ICommonTransaction
Modified: CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/file.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/file.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/file.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -43,8 +43,8 @@
CommonStoreTransaction, CommonHomeChild, CommonObjectResource,\
CommonStubResource
from txdav.common.icommondatastore import NoSuchObjectResourceError, InternalDataStoreError
-from txdav.datastore.file import hidden, writeOperation
-from txdav.propertystore.base import PropertyName
+from txdav.base.datastore.file import hidden, writeOperation
+from txdav.base.propertystore.base import PropertyName
from twistedcaldav import customxml, carddavxml
Modified: CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/sql.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/sql.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/sql.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -40,7 +40,7 @@
from txdav.common.datastore.sql_tables import ADDRESSBOOK_TABLE,\
ADDRESSBOOK_BIND_TABLE, ADDRESSBOOK_OBJECT_REVISIONS_TABLE,\
ADDRESSBOOK_OBJECT_TABLE
-from txdav.propertystore.base import PropertyName
+from txdav.base.propertystore.base import PropertyName
from zope.interface.declarations import implements
Modified: CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/test/common.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/test/common.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/test/common.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -24,7 +24,7 @@
)
from txdav.idav import IPropertyStore, IDataStore
-from txdav.propertystore.base import PropertyName
+from txdav.base.propertystore.base import PropertyName
from txdav.common.icommondatastore import (
HomeChildNameAlreadyExistsError, ICommonTransaction
Added: CalendarServer/branches/generic-sqlstore/txdav/base/__init__.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/base/__init__.py (rev 0)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/__init__.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -0,0 +1,19 @@
+##
+# Copyright (c) 2010 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.
+##
+
+"""
+Base DAV store.
+"""
Deleted: CalendarServer/branches/generic-sqlstore/txdav/base/datastore/file.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/datastore/file.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/datastore/file.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -1,288 +0,0 @@
-# -*- test-case-name: txdav -*-
-##
-# Copyright (c) 2010 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.
-##
-
-
-"""
-Common utility functions for a file based datastore.
-"""
-
-from twext.python.log import LoggingMixIn
-from txdav.idav import IDataStoreResource
-from txdav.idav import AlreadyFinishedError
-from txdav.propertystore.base import PropertyName
-
-from twext.web2.dav.element.rfc2518 import GETContentType
-from twext.web2.dav.resource import TwistedGETContentMD5
-
-
-
-from zope.interface.declarations import implements
-
-def isValidName(name):
- """
- Determine if the given string is a valid name. i.e. does it conflict with
- any of the other entities which may be on the filesystem?
-
- @param name: a name which might be given to a calendar.
- """
- return not name.startswith(".")
-
-
-def hidden(path):
- return path.sibling('.' + path.basename())
-
-
-_unset = object()
-
-class cached(object):
- """
- This object is a decorator for a 0-argument method which should be called
- only once, and its result cached so that future invocations just return the
- same result without calling the underlying method again.
-
- @ivar thunk: the function to call to generate a cached value.
- """
-
- def __init__(self, thunk):
- self.thunk = thunk
-
-
- def __get__(self, oself, owner):
- def inner():
- cacheKey = "_" + self.thunk.__name__ + "_cached"
- cached = getattr(oself, cacheKey, _unset)
- if cached is _unset:
- value = self.thunk(oself)
- setattr(oself, cacheKey, value)
- return value
- else:
- return cached
- return inner
-
-
-
-def writeOperation(thunk):
- # FIXME: tests
- def inner(self, *a, **kw):
- if self._transaction._termination is not None:
- raise RuntimeError(
- "%s.%s is a write operation, but transaction already %s"
- % (self, thunk.__name__, self._transaction._termination))
- return thunk(self, *a, **kw)
- return inner
-
-
-
-class DataStore(LoggingMixIn):
- """
- Generic data store.
- """
-
- _transactionClass = None # Derived class must set this
-
- def __init__(self, path):
- """
- Create a calendar store.
-
- @param path: a L{FilePath} pointing at a directory on disk.
- """
- self._path = path
-
-# if not path.isdir():
- # FIXME: Add DataStoreNotFoundError?
-# raise NotFoundError("No such data store")
-
- def __repr__(self):
- return "<%s: %s>" % (self.__class__.__name__, self._path.path)
-
- def newTransaction(self, name='no name'):
- """
- Create a new transaction.
-
- @see Transaction
- """
- return self._transactionClass(self)
-
-
-
-class _CommitTracker(object):
- """
- Diagnostic tool to find transactions that were never committed.
- """
-
- def __init__(self, name):
- self.name = name
- self.done = False
- self.info = []
-
- def __del__(self):
- if not self.done and self.info:
- print '**** UNCOMMITTED TRANSACTION (%s) BEING GARBAGE COLLECTED ****' % (
- self.name,
- )
- for info in self.info:
- print ' ', info
- print '---- END OF OPERATIONS'
-
-
-
-class DataStoreTransaction(LoggingMixIn):
- """
- In-memory implementation of a data store transaction.
- """
-
- def __init__(self, dataStore, name):
- """
- Initialize a transaction; do not call this directly, instead call
- L{CalendarStore.newTransaction}.
-
- @param calendarStore: The store that created this transaction.
-
- @type calendarStore: L{CalendarStore}
- """
- self._dataStore = dataStore
- self._termination = None
- self._operations = []
- self._postCommitOperations = []
- self._tracker = _CommitTracker(name)
-
-
- def store(self):
- return self._dataStore
-
- def addOperation(self, operation, name):
- self._operations.append(operation)
- self._tracker.info.append(name)
-
-
- def _terminate(self, mode):
- """
- Check to see if this transaction has already been terminated somehow,
- either via committing or aborting, and if not, note that it has been
- terminated.
-
- @param mode: The manner of the termination of this transaction.
-
- @type mode: C{str}
-
- @raise AlreadyFinishedError: This transaction has already been
- terminated.
- """
- if self._termination is not None:
- raise AlreadyFinishedError("already %s" % (self._termination,))
- self._termination = mode
- self._tracker.done = True
-
-
- def abort(self):
- self._terminate("aborted")
-
-
- def commit(self):
- self._terminate("committed")
-
- self.committed = True
- undos = []
-
- for operation in self._operations:
- try:
- undo = operation()
- if undo is not None:
- undos.append(undo)
- except:
- self.log_debug("Undoing DataStoreTransaction")
- for undo in undos:
- try:
- undo()
- except:
- self.log_error("Cannot undo DataStoreTransaction")
- raise
-
- for operation in self._postCommitOperations:
- operation()
-
- def postCommit(self, operation):
- self._postCommitOperations.append(operation)
-
-class FileMetaDataMixin(object):
-
- implements(IDataStoreResource)
-
- def name(self):
- """
- Identify the name of the object
-
- @return: the name of this object.
- @rtype: C{str}
- """
-
- return self._path.basename()
-
- def contentType(self):
- """
- The content type of the object's content.
-
- @rtype: L{MimeType}
- """
- try:
- return self.properties()[PropertyName.fromElement(GETContentType)].mimeType()
- except KeyError:
- return None
-
- def md5(self):
- """
- The MD5 hex digest of this object's content.
-
- @rtype: C{str}
- """
- try:
- return str(self.properties()[PropertyName.fromElement(TwistedGETContentMD5)])
- except KeyError:
- return None
-
- def size(self):
- """
- The octet-size of this object's content.
-
- @rtype: C{int}
- """
- if self._path.exists():
- return self._path.getsize()
- else:
- return 0
-
- def created(self):
- """
- The creation date-time stamp of this object.
-
- @rtype: C{int}
- """
- if self._path.exists():
- return self._path.getmtime() # No creation time on POSIX
- else:
- return None
-
- def modified(self):
- """
- The last modification date-time stamp of this object.
-
- @rtype: C{int}
- """
- if self._path.exists():
- return self._path.getmtime()
- else:
- return None
Copied: CalendarServer/branches/generic-sqlstore/txdav/base/datastore/file.py (from rev 6184, CalendarServer/branches/generic-sqlstore/txdav/datastore/file.py)
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/base/datastore/file.py (rev 0)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/datastore/file.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -0,0 +1,259 @@
+# -*- test-case-name: txdav -*-
+##
+# Copyright (c) 2010 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.
+##
+
+
+"""
+Common utility functions for a file based datastore.
+"""
+
+from twext.python.log import LoggingMixIn
+from txdav.idav import IDataStoreResource
+from txdav.idav import AlreadyFinishedError
+from txdav.base.propertystore.base import PropertyName
+
+from twext.web2.dav.element.rfc2518 import GETContentType
+from twext.web2.dav.resource import TwistedGETContentMD5
+
+
+
+from zope.interface.declarations import implements
+
+def isValidName(name):
+ """
+ Determine if the given string is a valid name. i.e. does it conflict with
+ any of the other entities which may be on the filesystem?
+
+ @param name: a name which might be given to a calendar.
+ """
+ return not name.startswith(".")
+
+
+def hidden(path):
+ return path.sibling('.' + path.basename())
+
+
+def writeOperation(thunk):
+ # FIXME: tests
+ def inner(self, *a, **kw):
+ if self._transaction._termination is not None:
+ raise RuntimeError(
+ "%s.%s is a write operation, but transaction already %s"
+ % (self, thunk.__name__, self._transaction._termination))
+ return thunk(self, *a, **kw)
+ return inner
+
+
+
+class DataStore(LoggingMixIn):
+ """
+ Generic data store.
+ """
+
+ _transactionClass = None # Derived class must set this
+
+ def __init__(self, path):
+ """
+ Create a calendar store.
+
+ @param path: a L{FilePath} pointing at a directory on disk.
+ """
+ self._path = path
+
+# if not path.isdir():
+ # FIXME: Add DataStoreNotFoundError?
+# raise NotFoundError("No such data store")
+
+ def __repr__(self):
+ return "<%s: %s>" % (self.__class__.__name__, self._path.path)
+
+ def newTransaction(self, name='no name'):
+ """
+ Create a new transaction.
+
+ @see Transaction
+ """
+ return self._transactionClass(self)
+
+
+
+class _CommitTracker(object):
+ """
+ Diagnostic tool to find transactions that were never committed.
+ """
+
+ def __init__(self, name):
+ self.name = name
+ self.done = False
+ self.info = []
+
+ def __del__(self):
+ if not self.done and self.info:
+ print '**** UNCOMMITTED TRANSACTION (%s) BEING GARBAGE COLLECTED ****' % (
+ self.name,
+ )
+ for info in self.info:
+ print ' ', info
+ print '---- END OF OPERATIONS'
+
+
+
+class DataStoreTransaction(LoggingMixIn):
+ """
+ In-memory implementation of a data store transaction.
+ """
+
+ def __init__(self, dataStore, name):
+ """
+ Initialize a transaction; do not call this directly, instead call
+ L{CalendarStore.newTransaction}.
+
+ @param calendarStore: The store that created this transaction.
+
+ @type calendarStore: L{CalendarStore}
+ """
+ self._dataStore = dataStore
+ self._termination = None
+ self._operations = []
+ self._postCommitOperations = []
+ self._tracker = _CommitTracker(name)
+
+
+ def store(self):
+ return self._dataStore
+
+ def addOperation(self, operation, name):
+ self._operations.append(operation)
+ self._tracker.info.append(name)
+
+
+ def _terminate(self, mode):
+ """
+ Check to see if this transaction has already been terminated somehow,
+ either via committing or aborting, and if not, note that it has been
+ terminated.
+
+ @param mode: The manner of the termination of this transaction.
+
+ @type mode: C{str}
+
+ @raise AlreadyFinishedError: This transaction has already been
+ terminated.
+ """
+ if self._termination is not None:
+ raise AlreadyFinishedError("already %s" % (self._termination,))
+ self._termination = mode
+ self._tracker.done = True
+
+
+ def abort(self):
+ self._terminate("aborted")
+
+
+ def commit(self):
+ self._terminate("committed")
+
+ self.committed = True
+ undos = []
+
+ for operation in self._operations:
+ try:
+ undo = operation()
+ if undo is not None:
+ undos.append(undo)
+ except:
+ self.log_debug("Undoing DataStoreTransaction")
+ for undo in undos:
+ try:
+ undo()
+ except:
+ self.log_error("Cannot undo DataStoreTransaction")
+ raise
+
+ for operation in self._postCommitOperations:
+ operation()
+
+ def postCommit(self, operation):
+ self._postCommitOperations.append(operation)
+
+class FileMetaDataMixin(object):
+
+ implements(IDataStoreResource)
+
+ def name(self):
+ """
+ Identify the name of the object
+
+ @return: the name of this object.
+ @rtype: C{str}
+ """
+
+ return self._path.basename()
+
+ def contentType(self):
+ """
+ The content type of the object's content.
+
+ @rtype: L{MimeType}
+ """
+ try:
+ return self.properties()[PropertyName.fromElement(GETContentType)].mimeType()
+ except KeyError:
+ return None
+
+ def md5(self):
+ """
+ The MD5 hex digest of this object's content.
+
+ @rtype: C{str}
+ """
+ try:
+ return str(self.properties()[PropertyName.fromElement(TwistedGETContentMD5)])
+ except KeyError:
+ return None
+
+ def size(self):
+ """
+ The octet-size of this object's content.
+
+ @rtype: C{int}
+ """
+ if self._path.exists():
+ return self._path.getsize()
+ else:
+ return 0
+
+ def created(self):
+ """
+ The creation date-time stamp of this object.
+
+ @rtype: C{int}
+ """
+ if self._path.exists():
+ return self._path.getmtime() # No creation time on POSIX
+ else:
+ return None
+
+ def modified(self):
+ """
+ The last modification date-time stamp of this object.
+
+ @rtype: C{int}
+ """
+ if self._path.exists():
+ return self._path.getmtime()
+ else:
+ return None
Copied: CalendarServer/branches/generic-sqlstore/txdav/base/datastore/sql.py (from rev 6168, CalendarServer/branches/generic-sqlstore/txdav/datastore/sql.py)
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/base/datastore/sql.py (rev 0)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/datastore/sql.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -0,0 +1,85 @@
+##
+# Copyright (c) 2010 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.
+##
+
+"""
+Logic common to SQL implementations.
+"""
+
+from inspect import getargspec
+
+def _getarg(argname, argspec, args, kw):
+ """
+ Get an argument from some arguments.
+
+ @param argname: The name of the argument to retrieve.
+
+ @param argspec: The result of L{inspect.getargspec}.
+
+ @param args: positional arguments passed to the function specified by
+ argspec.
+
+ @param kw: keyword arguments passed to the function specified by
+ argspec.
+
+ @return: The value of the argument named by 'argname'.
+ """
+ argnames = argspec[0]
+ try:
+ argpos = argnames.index(argname)
+ except ValueError:
+ argpos = None
+ if argpos is not None:
+ if len(args) > argpos:
+ return args[argpos]
+ if argname in kw:
+ return kw[argname]
+ else:
+ raise TypeError("could not find key argument %r in %r/%r (%r)" %
+ (argname, args, kw, argpos)
+ )
+
+
+
+def memoized(keyArgument, memoAttribute):
+ """
+ Decorator which memoizes the result of a method on that method's instance.
+
+ @param keyArgument: The name of the 'key' argument.
+
+ @type keyArgument: C{str}
+
+ @param memoAttribute: The name of the attribute on the instance which
+ should be used for memoizing the result of this method; the attribute
+ itself must be a dictionary.
+
+ @type memoAttribute: C{str}
+ """
+ def decorate(thunk):
+ spec = getargspec(thunk)
+ 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
+ return result
+ return outer
+ return decorate
+
+
Modified: CalendarServer/branches/generic-sqlstore/txdav/base/datastore/subpostgres.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/datastore/subpostgres.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/datastore/subpostgres.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -1,4 +1,4 @@
-# -*- test-case-name: txdav.datastore.test.test_subpostgres -*-
+# -*- test-case-name: txdav.base.datastore.test.test_subpostgres -*-
##
# Copyright (c) 2010 Apple Inc. All rights reserved.
#
Modified: CalendarServer/branches/generic-sqlstore/txdav/base/datastore/test/test_subpostgres.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/datastore/test/test_subpostgres.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/datastore/test/test_subpostgres.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -15,14 +15,14 @@
##
"""
-Tests for txdav.datastore.subpostgres.
+Tests for txdav.base.datastore.subpostgres.
"""
from twisted.trial.unittest import TestCase
from twext.python.filepath import CachingFilePath
-from txdav.datastore.subpostgres import PostgresService
+from txdav.base.datastore.subpostgres import PostgresService
from twisted.internet.defer import inlineCallbacks, Deferred
from twisted.application.service import Service
Copied: CalendarServer/branches/generic-sqlstore/txdav/base/datastore/util.py (from rev 6184, CalendarServer/branches/generic-sqlstore/txdav/datastore/util.py)
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/base/datastore/util.py (rev 0)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/datastore/util.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -0,0 +1,47 @@
+# -*- test-case-name: txcaldav.calendarstore.test.test_file -*-
+##
+# Copyright (c) 2010 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.
+##
+
+"""
+Common utility functions for a datastores.
+"""
+
+_unset = object()
+
+class cached(object):
+ """
+ This object is a decorator for a 0-argument method which should be called
+ only once, and its result cached so that future invocations just return the
+ same result without calling the underlying method again.
+
+ @ivar thunk: the function to call to generate a cached value.
+ """
+
+ def __init__(self, thunk):
+ self.thunk = thunk
+
+
+ def __get__(self, oself, owner):
+ def inner():
+ cacheKey = "_" + self.thunk.__name__ + "_cached"
+ cached = getattr(oself, cacheKey, _unset)
+ if cached is _unset:
+ value = self.thunk(oself)
+ setattr(oself, cacheKey, value)
+ return value
+ else:
+ return cached
+ return inner
Modified: CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/none.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/propertystore/none.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/none.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -22,7 +22,7 @@
"PropertyStore",
]
-from txdav.propertystore.base import AbstractPropertyStore, validKey
+from txdav.base.propertystore.base import AbstractPropertyStore, validKey
class PropertyStore(AbstractPropertyStore):
"""
Copied: CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/sql.py (from rev 6169, CalendarServer/branches/generic-sqlstore/txdav/propertystore/sql.py)
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/sql.py (rev 0)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/sql.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -0,0 +1,81 @@
+# -*- test-case-name: txcaldav.calendarstore.test.test_postgres -*-
+##
+# Copyright (c) 2010 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.
+##
+
+"""
+PostgreSQL data store.
+"""
+
+__all__ = [
+ "PropertyStore",
+]
+
+from txdav.base.propertystore.base import AbstractPropertyStore, PropertyName,\
+ validKey
+
+from twext.web2.dav.davxml import WebDAVDocument
+
+class PropertyStore(AbstractPropertyStore):
+
+ def __init__(self, defaultuser, txn, resourceID):
+ super(PropertyStore, self).__init__(defaultuser)
+ self._txn = txn
+ self._resourceID = resourceID
+
+
+ def _getitem_uid(self, key, uid):
+ validKey(key)
+ rows = self._txn.execSQL(
+ "select VALUE from RESOURCE_PROPERTY where "
+ "RESOURCE_ID = %s and NAME = %s and VIEWER_UID = %s",
+ [self._resourceID, key.toString(), uid]
+ )
+ if not rows:
+ raise KeyError(key)
+ return WebDAVDocument.fromString(rows[0][0]).root_element
+
+
+ def _setitem_uid(self, key, value, uid):
+ validKey(key)
+ try:
+ self._delitem_uid(key, uid)
+ except KeyError:
+ pass
+ self._txn.execSQL(
+ "insert into RESOURCE_PROPERTY "
+ "(RESOURCE_ID, NAME, VALUE, VIEWER_UID) values (%s, %s, %s, %s)",
+ [self._resourceID, key.toString(), value.toxml(), uid]
+ )
+
+
+ def _delitem_uid(self, key, uid):
+ validKey(key)
+ self._txn.execSQL(
+ "delete from RESOURCE_PROPERTY where VIEWER_UID = %s"
+ "and RESOURCE_ID = %s AND NAME = %s",
+ [uid, self._resourceID, key.toString()],
+ raiseOnZeroRowCount=lambda:KeyError(key)
+ )
+
+
+ def _keys_uid(self, uid):
+ rows = self._txn.execSQL(
+ "select NAME from RESOURCE_PROPERTY where "
+ "VIEWER_UID = %s and RESOURCE_ID = %s",
+ [uid, self._resourceID]
+ )
+ for row in rows:
+ yield PropertyName.fromString(row[0])
Deleted: CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/base.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/propertystore/test/base.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/base.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -1,264 +0,0 @@
-##
-# Copyright (c) 2010 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.
-##
-
-"""
-Generic property store tests.
-"""
-
-__all__ = [
- "PropertyStoreTest",
- "propertyName",
- "propertyValue",
-]
-
-
-from zope.interface.verify import verifyObject, BrokenMethodImplementation
-
-from twisted.trial import unittest
-
-from twext.web2.dav import davxml
-
-from txdav.idav import IPropertyStore
-from txdav.propertystore.base import PropertyName
-
-
-class PropertyStoreTest(unittest.TestCase):
- # Subclass must define self.propertyStore in setUp().
-
- def test_interface(self):
- try:
- verifyObject(IPropertyStore, self.propertyStore)
- except BrokenMethodImplementation, e:
- self.fail(e)
-
- def test_set_get_contains(self):
- store = self.propertyStore
-
- name = propertyName("test")
- value = propertyValue("Hello, World!")
-
- store[name] = value
- self.assertEquals(store.get(name, None), value)
- self.failUnless(name in store)
-
- def test_delete_get_contains(self):
- store = self.propertyStore
-
- name = propertyName("test")
- value = propertyValue("Hello, World!")
-
- store[name] = value
- del store[name]
- self.assertEquals(store.get(name, None), None)
- self.failIf(name in store)
-
- def test_peruser(self):
- store1 = self.propertyStore1
- store2 = self.propertyStore2
-
- name = propertyName("test")
- value1 = propertyValue("Hello, World1!")
- value2 = propertyValue("Hello, World2!")
-
- store1[name] = value1
- store1.flush()
- self.assertEquals(store1.get(name, None), value1)
- self.assertEquals(store2.get(name, None), None)
- self.failUnless(name in store1)
- self.failIf(name in store2)
-
- store2[name] = value2
- store2.flush()
- self.assertEquals(store1.get(name, None), value1)
- self.assertEquals(store2.get(name, None), value2)
- self.failUnless(name in store1)
- self.failUnless(name in store2)
-
- del store2[name]
- store2.flush()
- self.assertEquals(store1.get(name, None), value1)
- self.assertEquals(store2.get(name, None), None)
- self.failUnless(name in store1)
- self.failIf(name in store2)
-
- del store1[name]
- store1.flush()
- self.assertEquals(store1.get(name, None), None)
- self.assertEquals(store2.get(name, None), None)
- self.failIf(name in store1)
- self.failIf(name in store2)
-
- def test_peruser_shadow(self):
- store1 = self.propertyStore1
- store2 = self.propertyStore2
-
- name = propertyName("shadow")
-
- store1.setSpecialProperties((name,), ())
- store2.setSpecialProperties((name,), ())
-
- value1 = propertyValue("Hello, World1!")
- value2 = propertyValue("Hello, World2!")
-
- store1[name] = value1
- store1.flush()
- self.assertEquals(store1.get(name, None), value1)
- self.assertEquals(store2.get(name, None), value1)
- self.failUnless(name in store1)
- self.failUnless(name in store2)
-
- store2[name] = value2
- store2.flush()
- self.assertEquals(store1.get(name, None), value1)
- self.assertEquals(store2.get(name, None), value2)
- self.failUnless(name in store1)
- self.failUnless(name in store2)
-
- del store2[name]
- store2.flush()
- self.assertEquals(store1.get(name, None), value1)
- self.assertEquals(store2.get(name, None), value1)
- self.failUnless(name in store1)
- self.failUnless(name in store2)
-
- del store1[name]
- store1.flush()
- self.assertEquals(store1.get(name, None), None)
- self.assertEquals(store2.get(name, None), None)
- self.failIf(name in store1)
- self.failIf(name in store2)
-
-
- def test_peruser_global(self):
- store1 = self.propertyStore1
- store2 = self.propertyStore2
-
- name = propertyName("global")
-
- store1.setSpecialProperties((), (name,))
- store2.setSpecialProperties((), (name,))
-
- value1 = propertyValue("Hello, World1!")
- value2 = propertyValue("Hello, World2!")
-
- store1[name] = value1
- store1.flush()
- self.assertEquals(store1.get(name, None), value1)
- self.assertEquals(store2.get(name, None), value1)
- self.failUnless(name in store1)
- self.failUnless(name in store2)
-
- store2[name] = value2
- store2.flush()
- self.assertEquals(store1.get(name, None), value2)
- self.assertEquals(store2.get(name, None), value2)
- self.failUnless(name in store1)
- self.failUnless(name in store2)
-
- del store2[name]
- store2.flush()
- self.assertEquals(store1.get(name, None), None)
- self.assertEquals(store2.get(name, None), None)
- self.failIf(name in store1)
- self.failIf(name in store2)
-
-
- def test_iteration(self):
- store = self.propertyStore
-
- value = propertyValue("Hello, World!")
-
- names = set(propertyName(str(i)) for i in (1,2,3,4))
-
- for name in names:
- store[name] = value
-
- self.assertEquals(set(store.keys()), names)
- self.assertEquals(len(store), len(names))
-
- def test_delete_none(self):
- def doDelete():
- del self.propertyStore[propertyName("xyzzy")]
-
- self.assertRaises(KeyError, doDelete)
-
- def test_keyInPropertyName(self):
- store = self.propertyStore
-
- def doGet():
- store["xyzzy"]
-
- def doSet():
- store["xyzzy"] = propertyValue("Hello, World!")
-
- def doDelete():
- del store["xyzzy"]
-
- def doContains():
- "xyzzy" in store
-
- self.assertRaises(TypeError, doGet)
- self.assertRaises(TypeError, doSet)
- self.assertRaises(TypeError, doDelete)
- self.assertRaises(TypeError, doContains)
-
- def test_flush(self):
- store = self.propertyStore
-
- name = propertyName("test")
- value = propertyValue("Hello, World!")
-
- #
- # Set value flushes correctly
- #
- store[name] = value
-
- store.flush()
- store.abort()
-
- self.assertEquals(store.get(name, None), value)
- self.assertEquals(len(store), 1)
-
- #
- # Deleted value flushes correctly
- #
- del store[name]
-
- store.flush()
- store.abort()
-
- self.assertEquals(store.get(name, None), None)
- self.assertEquals(len(store), 0)
-
- def test_abort(self):
- store = self.propertyStore
-
- name = propertyName("test")
- value = propertyValue("Hello, World!")
-
- store[name] = value
-
- store.abort()
-
- self.assertEquals(store.get(name, None), None)
- self.assertEquals(len(store), 0)
-
-
-def propertyName(name):
- return PropertyName("http://calendarserver.org/ns/test/", name)
-
-def propertyValue(value):
- return davxml.ResponseDescription(value)
Copied: CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/base.py (from rev 6169, CalendarServer/branches/generic-sqlstore/txdav/propertystore/test/base.py)
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/base.py (rev 0)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/base.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -0,0 +1,319 @@
+##
+# Copyright (c) 2010 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.
+##
+
+"""
+Generic property store tests.
+"""
+
+__all__ = [
+ "PropertyStoreTest",
+ "propertyName",
+ "propertyValue",
+]
+
+
+from zope.interface.verify import verifyObject, BrokenMethodImplementation
+
+from twisted.trial import unittest
+
+from twext.web2.dav import davxml
+
+from txdav.idav import IPropertyStore
+from txdav.base.propertystore.base import PropertyName
+
+
+class PropertyStoreTest(unittest.TestCase):
+ # Subclass must define self.propertyStore in setUp().
+
+ def _preTest(self):
+ self.addCleanup(self._postTest)
+ def _postTest(self):
+ pass
+ def _changed(self, store):
+ store.flush()
+ def _abort(self, store):
+ store.abort()
+
+ def test_interface(self):
+ try:
+ self._preTest()
+ verifyObject(IPropertyStore, self.propertyStore)
+ except BrokenMethodImplementation, e:
+ self.fail(e)
+
+ def test_set_get_contains(self):
+
+ self._preTest()
+ store = self.propertyStore
+
+ name = propertyName("test")
+ value = propertyValue("Hello, World!")
+
+ # Test with commit after change
+ store[name] = value
+ self._changed(store)
+ self.assertEquals(store.get(name, None), value)
+ self.failUnless(name in store)
+
+ # Test without commit after change
+ value = propertyValue("Hello, Universe!")
+ store[name] = value
+ self.assertEquals(store.get(name, None), value)
+ self.failUnless(name in store)
+
+ def test_delete_get_contains(self):
+
+ self._preTest()
+ store = self.propertyStore
+
+ # Test with commit after change
+ name = propertyName("test")
+ value = propertyValue("Hello, World!")
+
+ store[name] = value
+ self._changed(store)
+
+ del store[name]
+ self._changed(store)
+
+ self.assertEquals(store.get(name, None), None)
+ self.failIf(name in store)
+
+ # Test without commit after change
+ name = propertyName("test")
+ value = propertyValue("Hello, Universe!")
+
+ store[name] = value
+ self._changed(store)
+
+ del store[name]
+
+ self.assertEquals(store.get(name, None), None)
+ self.failIf(name in store)
+
+ def test_peruser(self):
+
+ self._preTest()
+ store1 = self.propertyStore1
+ store2 = self.propertyStore2
+
+ name = propertyName("test")
+ value1 = propertyValue("Hello, World1!")
+ value2 = propertyValue("Hello, World2!")
+
+ store1[name] = value1
+ self._changed(store1)
+ self.assertEquals(store1.get(name, None), value1)
+ self.assertEquals(store2.get(name, None), None)
+ self.failUnless(name in store1)
+ self.failIf(name in store2)
+
+ store2[name] = value2
+ self._changed(store2)
+ self.assertEquals(store1.get(name, None), value1)
+ self.assertEquals(store2.get(name, None), value2)
+ self.failUnless(name in store1)
+ self.failUnless(name in store2)
+
+ del store2[name]
+ self._changed(store2)
+ self.assertEquals(store1.get(name, None), value1)
+ self.assertEquals(store2.get(name, None), None)
+ self.failUnless(name in store1)
+ self.failIf(name in store2)
+
+ del store1[name]
+ self._changed(store1)
+ self.assertEquals(store1.get(name, None), None)
+ self.assertEquals(store2.get(name, None), None)
+ self.failIf(name in store1)
+ self.failIf(name in store2)
+
+ def test_peruser_shadow(self):
+
+ self._preTest()
+ store1 = self.propertyStore1
+ store2 = self.propertyStore2
+
+ name = propertyName("shadow")
+
+ store1.setSpecialProperties((name,), ())
+ store2.setSpecialProperties((name,), ())
+
+ value1 = propertyValue("Hello, World1!")
+ value2 = propertyValue("Hello, World2!")
+
+ store1[name] = value1
+ self._changed(store1)
+ self.assertEquals(store1.get(name, None), value1)
+ self.assertEquals(store2.get(name, None), value1)
+ self.failUnless(name in store1)
+ self.failUnless(name in store2)
+
+ store2[name] = value2
+ self._changed(store2)
+ self.assertEquals(store1.get(name, None), value1)
+ self.assertEquals(store2.get(name, None), value2)
+ self.failUnless(name in store1)
+ self.failUnless(name in store2)
+
+ del store2[name]
+ self._changed(store2)
+ self.assertEquals(store1.get(name, None), value1)
+ self.assertEquals(store2.get(name, None), value1)
+ self.failUnless(name in store1)
+ self.failUnless(name in store2)
+
+ del store1[name]
+ self._changed(store1)
+ self.assertEquals(store1.get(name, None), None)
+ self.assertEquals(store2.get(name, None), None)
+ self.failIf(name in store1)
+ self.failIf(name in store2)
+
+
+ def test_peruser_global(self):
+
+ self._preTest()
+ store1 = self.propertyStore1
+ store2 = self.propertyStore2
+
+ name = propertyName("global")
+
+ store1.setSpecialProperties((), (name,))
+ store2.setSpecialProperties((), (name,))
+
+ value1 = propertyValue("Hello, World1!")
+ value2 = propertyValue("Hello, World2!")
+
+ store1[name] = value1
+ self._changed(store1)
+ self.assertEquals(store1.get(name, None), value1)
+ self.assertEquals(store2.get(name, None), value1)
+ self.failUnless(name in store1)
+ self.failUnless(name in store2)
+
+ store2[name] = value2
+ self._changed(store2)
+ self.assertEquals(store1.get(name, None), value2)
+ self.assertEquals(store2.get(name, None), value2)
+ self.failUnless(name in store1)
+ self.failUnless(name in store2)
+
+ del store2[name]
+ self._changed(store2)
+ self.assertEquals(store1.get(name, None), None)
+ self.assertEquals(store2.get(name, None), None)
+ self.failIf(name in store1)
+ self.failIf(name in store2)
+
+
+ def test_iteration(self):
+
+ self._preTest()
+ store = self.propertyStore
+
+ value = propertyValue("Hello, World!")
+
+ names = set(propertyName(str(i)) for i in (1,2,3,4))
+
+ for name in names:
+ store[name] = value
+
+ self.assertEquals(set(store.keys()), names)
+ self.assertEquals(len(store), len(names))
+
+ def test_delete_none(self):
+
+ self._preTest()
+ def doDelete():
+ del self.propertyStore[propertyName("xyzzy")]
+
+ self.assertRaises(KeyError, doDelete)
+
+ def test_keyInPropertyName(self):
+
+ self._preTest()
+ store = self.propertyStore
+
+ def doGet():
+ store["xyzzy"]
+
+ def doSet():
+ store["xyzzy"] = propertyValue("Hello, World!")
+
+ def doDelete():
+ del store["xyzzy"]
+
+ def doContains():
+ return "xyzzy" in store
+
+ self.assertRaises(TypeError, doGet)
+ self.assertRaises(TypeError, doSet)
+ self.assertRaises(TypeError, doDelete)
+ self.assertRaises(TypeError, doContains)
+
+ def test_flush(self):
+
+ self._preTest()
+ store = self.propertyStore
+
+ name = propertyName("test")
+ value = propertyValue("Hello, World!")
+
+ #
+ # Set value flushes correctly
+ #
+ store[name] = value
+
+ self._changed(store)
+ self._abort(store)
+
+ self.assertEquals(store.get(name, None), value)
+ self.assertEquals(len(store), 1)
+
+ #
+ # Deleted value flushes correctly
+ #
+ del store[name]
+
+ self._changed(store)
+ self._abort(store)
+
+ self.assertEquals(store.get(name, None), None)
+ self.assertEquals(len(store), 0)
+
+ def test_abort(self):
+
+ self._preTest()
+ store = self.propertyStore
+
+ name = propertyName("test")
+ value = propertyValue("Hello, World!")
+
+ store[name] = value
+
+ self._abort(store)
+
+ self.assertEquals(store.get(name, None), None)
+ self.assertEquals(len(store), 0)
+
+
+def propertyName(name):
+ return PropertyName("http://calendarserver.org/ns/test/", name)
+
+def propertyValue(value):
+ return davxml.ResponseDescription(value)
Modified: CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_base.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/propertystore/test/test_base.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_base.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -23,7 +23,7 @@
from twisted.trial import unittest
from txdav.idav import IPropertyName
-from txdav.propertystore.base import PropertyName
+from txdav.base.propertystore.base import PropertyName
class PropertyNameTest(unittest.TestCase):
Modified: CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_none.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/propertystore/test/test_none.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_none.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -18,9 +18,9 @@
Property store tests.
"""
-from txdav.propertystore.none import PropertyStore
+from txdav.base.propertystore.none import PropertyStore
-from txdav.propertystore.test import base
+from txdav.base.propertystore.test import base
class PropertyStoreTest(base.PropertyStoreTest):
def setUp(self):
Copied: CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_sql.py (from rev 6169, CalendarServer/branches/generic-sqlstore/txdav/propertystore/test/test_sql.py)
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_sql.py (rev 0)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_sql.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -0,0 +1,190 @@
+##
+# Copyright (c) 2010 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.
+##
+
+"""
+Tests for txcaldav.calendarstore.postgres, mostly based on
+L{txcaldav.calendarstore.test.common}.
+"""
+
+
+from twext.python.filepath import CachingFilePath
+
+from twisted.internet import reactor
+from twisted.internet.defer import Deferred, inlineCallbacks, succeed
+from twisted.internet.task import deferLater
+from twisted.python import log
+
+from txdav.common.datastore.sql import v1_schema
+from txcaldav.calendarstore.test.common import StubNotifierFactory
+
+from txdav.common.datastore.sql import CommonDataStore
+from txdav.base.datastore.subpostgres import PostgresService
+from txdav.base.propertystore.base import PropertyName
+from txdav.base.propertystore.test import base
+
+try:
+ from txdav.base.propertystore.sql import PropertyStore
+except ImportError, e:
+ PropertyStore = None
+ importErrorMessage = str(e)
+
+
+
+class StoreBuilder(object):
+ """
+ Test-fixture-builder which can construct a PostgresStore.
+ """
+ sharedService = None
+ currentTestID = None
+
+ SHARED_DB_PATH = "../_test_postgres_db"
+
+ def buildStore(self, testCase, notifierFactory):
+ """
+ Do the necessary work to build a store for a particular test case.
+
+ @return: a L{Deferred} which fires with an L{IDataStore}.
+ """
+ currentTestID = testCase.id()
+ dbRoot = CachingFilePath(self.SHARED_DB_PATH)
+ if self.sharedService is None:
+ ready = Deferred()
+ def getReady(connectionFactory):
+ attachmentRoot = dbRoot.child("attachments")
+ try:
+ attachmentRoot.createDirectory()
+ except OSError:
+ pass
+ try:
+ self.store = CommonDataStore(
+ lambda label=None: connectionFactory(
+ label or currentTestID
+ ),
+ notifierFactory,
+ attachmentRoot
+ )
+ except:
+ ready.errback()
+ raise
+ else:
+ self.cleanDatabase(testCase)
+ ready.callback(self.store)
+ return self.store
+ self.sharedService = PostgresService(
+ dbRoot, getReady, v1_schema, "caldav", resetSchema=True,
+ testMode=True
+ )
+ self.sharedService.startService()
+ def startStopping():
+ log.msg("Starting stopping.")
+ self.sharedService.unpauseMonitor()
+ return self.sharedService.stopService()
+ reactor.addSystemEventTrigger(#@UndefinedVariable
+ "before", "shutdown", startStopping)
+ result = ready
+ else:
+ self.store.notifierFactory = notifierFactory
+ self.cleanDatabase(testCase)
+ result = succeed(self.store)
+
+ def cleanUp():
+ # FIXME: clean up any leaked connections and report them with an
+ # immediate test failure.
+ def stopit():
+ self.sharedService.pauseMonitor()
+ return deferLater(reactor, 0.1, stopit)
+ testCase.addCleanup(cleanUp)
+ return result
+
+
+ def cleanDatabase(self, testCase):
+ cleanupConn = self.store.connectionFactory(
+ "%s schema-cleanup" % (testCase.id(),)
+ )
+ cursor = cleanupConn.cursor()
+ tables = ['INVITE',
+ 'RESOURCE_PROPERTY',
+ 'ATTACHMENT',
+ 'ADDRESSBOOK_OBJECT',
+ 'CALENDAR_OBJECT',
+ 'CALENDAR_BIND',
+ 'ADDRESSBOOK_BIND',
+ 'CALENDAR',
+ 'ADDRESSBOOK',
+ 'CALENDAR_HOME',
+ 'ADDRESSBOOK_HOME',
+ 'NOTIFICATION',
+ 'NOTIFICATION_HOME']
+ for table in tables:
+ try:
+ cursor.execute("delete from "+table)
+ except:
+ log.err()
+ cleanupConn.commit()
+ cleanupConn.close()
+
+
+
+theStoreBuilder = StoreBuilder()
+buildStore = theStoreBuilder.buildStore
+
+
+class PropertyStoreTest(base.PropertyStoreTest):
+
+ def _preTest(self):
+ self._txn = self.store.newTransaction()
+ self.propertyStore = self.propertyStore1 = PropertyStore(
+ "user01", self._txn, 1
+ )
+ self.propertyStore2 = PropertyStore("user01", self._txn, 1)
+ self.propertyStore2._setPerUserUID("user02")
+
+ self.addCleanup(self._postTest)
+
+ def _postTest(self):
+ if hasattr(self, "_txn"):
+ self._txn.commit()
+ delattr(self, "_txn")
+ self.propertyStore = self.propertyStore1 = self.propertyStore2 = None
+
+ def _changed(self, store):
+ if hasattr(self, "_txn"):
+ self._txn.commit()
+ delattr(self, "_txn")
+ self._txn = self.store.newTransaction()
+ self.propertyStore1._txn = self._txn
+ self.propertyStore2._txn = self._txn
+
+ def _abort(self, store):
+ if hasattr(self, "_txn"):
+ self._txn.abort()
+ delattr(self, "_txn")
+
+ self._txn = self.store.newTransaction()
+ self.propertyStore1._txn = self._txn
+ self.propertyStore2._txn = self._txn
+
+ @inlineCallbacks
+ def setUp(self):
+ self.notifierFactory = StubNotifierFactory()
+ self.store = yield buildStore(self, self.notifierFactory)
+
+if PropertyStore is None:
+ PropertyStoreTest.skip = importErrorMessage
+
+
+def propertyName(name):
+ return PropertyName("http://calendarserver.org/ns/test/", name)
Modified: CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_xattr.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/propertystore/test/test_xattr.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/test/test_xattr.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -21,12 +21,12 @@
from twext.python.filepath import CachingFilePath as FilePath
-from txdav.propertystore.base import PropertyName
+from txdav.base.propertystore.base import PropertyName
-from txdav.propertystore.test import base
+from txdav.base.propertystore.test import base
try:
- from txdav.propertystore.xattr import PropertyStore
+ from txdav.base.propertystore.xattr import PropertyStore
from xattr import xattr
except ImportError, e:
PropertyStore = None
Modified: CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/xattr.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/propertystore/xattr.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txdav/base/propertystore/xattr.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -1,4 +1,4 @@
-# -*- test-case-name: txdav.propertystore.test.test_xattr,txcaldav.calendarstore,txcarddav.addressbookstore -*-
+# -*- test-case-name: txdav.base.propertystore.test.test_xattr,txcaldav.calendarstore,txcarddav.addressbookstore -*-
##
# Copyright (c) 2010 Apple Inc. All rights reserved.
#
@@ -34,7 +34,7 @@
from twext.web2.dav.davxml import WebDAVDocument
-from txdav.propertystore.base import AbstractPropertyStore, PropertyName, validKey
+from txdav.base.propertystore.base import AbstractPropertyStore, PropertyName, validKey
from txdav.idav import PropertyStoreError
Modified: CalendarServer/branches/generic-sqlstore/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/common/datastore/file.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/txdav/common/datastore/file.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -38,12 +38,12 @@
ObjectResourceNameAlreadyExistsError, NoSuchObjectResourceError
from txdav.common.inotifications import INotificationCollection, \
INotificationObject
-from txdav.datastore.file import DataStoreTransaction, DataStore, writeOperation, \
+from txdav.base.datastore.file import DataStoreTransaction, DataStore, writeOperation, \
hidden, isValidName, FileMetaDataMixin
-from txdav.datastore.util import cached
+from txdav.base.datastore.util import cached
from txdav.idav import IDataStore
-from txdav.propertystore.base import PropertyName
-from txdav.propertystore.xattr import PropertyStore
+from txdav.base.propertystore.base import PropertyName
+from txdav.base.propertystore.xattr import PropertyStore
from errno import EEXIST, ENOENT
from zope.interface import implements, directlyProvides
Modified: CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -49,11 +49,11 @@
NoSuchObjectResourceError
from txdav.common.inotifications import INotificationCollection,\
INotificationObject
-from txdav.datastore.sql import memoized
-from txdav.datastore.util import cached
+from txdav.base.datastore.sql import memoized
+from txdav.base.datastore.util import cached
from txdav.idav import IDataStore, AlreadyFinishedError
-from txdav.propertystore.base import PropertyName
-from txdav.propertystore.sql import PropertyStore
+from txdav.base.propertystore.base import PropertyName
+from txdav.base.propertystore.sql import PropertyStore
from zope.interface.declarations import implements, directlyProvides
Modified: CalendarServer/branches/generic-sqlstore/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/common/datastore/test/util.py 2010-08-26 00:52:30 UTC (rev 6184)
+++ CalendarServer/branches/generic-sqlstore/txdav/common/datastore/test/util.py 2010-08-26 01:11:33 UTC (rev 6185)
@@ -29,7 +29,7 @@
from twisted.python import log
from txdav.common.datastore.sql import CommonDataStore, v1_schema
-from txdav.datastore.subpostgres import PostgresService,\
+from txdav.base.datastore.subpostgres import PostgresService,\
DiagnosticConnectionWrapper
def allInstancesOf(cls):
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100825/d8002034/attachment-0001.html>
More information about the calendarserver-changes
mailing list