[CalendarServer-changes] [6168] CalendarServer/branches/generic-sqlstore
source_changes at macosforge.org
source_changes at macosforge.org
Mon Aug 23 11:36:43 PDT 2010
Revision: 6168
http://trac.macosforge.org/projects/calendarserver/changeset/6168
Author: cdaboo at apple.com
Date: 2010-08-23 11:36:43 -0700 (Mon, 23 Aug 2010)
Log Message:
-----------
Moved data store and transaction into common.
Modified Paths:
--------------
CalendarServer/branches/generic-sqlstore/calendarserver/tap/util.py
CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/file.py
CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/postgres.py
CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/common.py
CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/test_postgres.py
CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/file.py
CalendarServer/branches/generic-sqlstore/txdav/common/datastore/file.py
Added Paths:
-----------
CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py
CalendarServer/branches/generic-sqlstore/txdav/datastore/sql.py
Modified: CalendarServer/branches/generic-sqlstore/calendarserver/tap/util.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/calendarserver/tap/util.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/calendarserver/tap/util.py 2010-08-23 18:36:43 UTC (rev 6168)
@@ -67,8 +67,9 @@
from calendarserver.webadmin.resource import WebAdminResource
from calendarserver.webcal.resource import WebCalendarResource
-from txdav.common.datastore.file import CommonDataStore
-from txcaldav.calendarstore.postgres import PostgresStore, v1_schema
+from txdav.common.datastore.sql import CommonDataStore as CommonSQLDataStore
+from txdav.common.datastore.file import CommonDataStore as CommonFileDataStore
+from txcaldav.calendarstore.postgres import v1_schema
from txdav.datastore.subpostgres import PostgresService
from twext.python.filepath import CachingFilePath
@@ -294,11 +295,10 @@
_dbRoot = CachingFilePath(config.DatabaseRoot)
_postgresService = PostgresService(_dbRoot, None, v1_schema, "caldav",
logFile=config.PostgresLogFile)
- _newStore = PostgresStore(_postgresService.produceConnection,
- notifierFactory, # config.EnableCalDAV, config.EnableCardDAV)
- _dbRoot.child("attachments"))
+ _newStore = CommonSQLDataStore(_postgresService.produceConnection,
+ notifierFactory, _dbRoot.child("attachments"), config.EnableCalDAV, config.EnableCardDAV)
else:
- _newStore = CommonDataStore(FilePath(config.DocumentRoot),
+ _newStore = CommonFileDataStore(FilePath(config.DocumentRoot),
notifierFactory, config.EnableCalDAV, config.EnableCardDAV)
if config.EnableCalDAV:
Modified: CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/file.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/file.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/file.py 2010-08-23 18:36:43 UTC (rev 6168)
@@ -125,7 +125,7 @@
return self._dataStore
- def created(self):
+ def createdHome(self):
self.createCalendarWithName("calendar")
defaultCal = self.calendarWithName("calendar")
props = defaultCal.properties()
Modified: CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/postgres.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/postgres.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/postgres.py 2010-08-23 18:36:43 UTC (rev 6168)
@@ -20,7 +20,6 @@
"""
__all__ = [
- "PostgresStore",
"PostgresCalendarHome",
"PostgresCalendar",
"PostgresCalendarObject",
@@ -34,10 +33,8 @@
from twistedcaldav.sharing import SharedCollectionRecord #@UnusedImport
-from inspect import getargspec
from zope.interface.declarations import implements
-from twisted.application.service import Service
from twisted.internet.error import ConnectionLost
from twisted.internet.interfaces import ITransport
from twisted.python import hashlib
@@ -47,7 +44,6 @@
from twext.web2.dav.element.rfc2518 import ResourceType
-from txdav.idav import IDataStore, AlreadyFinishedError
from txdav.common.inotifications import (INotificationCollection,
INotificationObject)
@@ -58,11 +54,10 @@
validateAddressBookComponent, dropboxIDFromCalendarObject, CalendarSyncTokenHelper,
AddressbookSyncTokenHelper)
from txdav.datastore.file import cached
+from txdav.datastore.sql import memoized
-from txcaldav.icalendarstore import (ICalendarTransaction, ICalendarHome,
- ICalendar, ICalendarObject, IAttachment)
-from txcarddav.iaddressbookstore import (IAddressBookTransaction,
- IAddressBookHome, IAddressBook, IAddressBookObject)
+from txcaldav.icalendarstore import (ICalendarHome, ICalendar, ICalendarObject, IAttachment)
+from txcarddav.iaddressbookstore import (IAddressBookHome, IAddressBook, IAddressBookObject)
from txdav.propertystore.base import AbstractPropertyStore, PropertyName
from txdav.propertystore.none import PropertyStore
@@ -74,6 +69,7 @@
from twistedcaldav import carddavxml
from twistedcaldav.config import config
+from twistedcaldav.caldavxml import ScheduleCalendarTransp, Opaque
from twistedcaldav.customxml import NotificationType
from twistedcaldav.dateops import normalizeForIndex
from twistedcaldav.index import IndexedSearchException, ReservationError,\
@@ -150,70 +146,6 @@
}
-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
-
-
-
class PropertyStore(AbstractPropertyStore):
def __init__(self, defaultuser, txn, resourceID):
@@ -1693,6 +1625,14 @@
return names
+ def createdHome(self):
+ self.createCalendarWithName("calendar")
+ defaultCal = self.calendarWithName("calendar")
+ props = defaultCal.properties()
+ props[PropertyName(*ScheduleCalendarTransp.qname())] = ScheduleCalendarTransp(
+ Opaque())
+ self.createCalendarWithName("inbox")
+
def calendars(self):
"""
Retrieve calendars contained in this calendar home.
@@ -2057,151 +1997,6 @@
-class PostgresTransaction(object):
- """
- Transaction implementation for postgres database.
- """
- implements(ICalendarTransaction, IAddressBookTransaction)
-
- def __init__(self, store, connection, notifierFactory, label):
- # print 'STARTING', label
- self._store = store
- self._connection = connection
- self._cursor = connection.cursor()
- self._completed = False
- self._calendarHomes = {}
- self._addressbookHomes = {}
- self._notificationHomes = {}
- self._postCommitOperations = []
- self._notifierFactory = notifierFactory
- self._label = label
-
-
- def store(self):
- return self._store
-
-
- def __repr__(self):
- return 'PG-TXN<%s>' % (self._label,)
-
-
- def execSQL(self, sql, args=[]):
- # print 'EXECUTE %s: %s' % (self._label, sql)
- self._cursor.execute(sql, args)
- if self._cursor.description:
- return self._cursor.fetchall()
- else:
- return None
-
-
- def __del__(self):
- if not self._completed:
- self._connection.rollback()
- self._connection.close()
-
-
- @memoized('uid', '_calendarHomes')
- def calendarHomeWithUID(self, uid, create=False):
- data = self.execSQL(
- "select RESOURCE_ID from CALENDAR_HOME where OWNER_UID = %s",
- [uid]
- )
- if not data:
- if not create:
- return None
- self.execSQL(
- "insert into CALENDAR_HOME (OWNER_UID) values (%s)",
- [uid]
- )
- home = self.calendarHomeWithUID(uid)
- home.createCalendarWithName("calendar")
- return home
- resid = data[0][0]
-
- if self._notifierFactory:
- notifier = self._notifierFactory.newNotifier(id=uid)
- else:
- notifier = None
-
- return PostgresCalendarHome(self, uid, resid, notifier)
-
-
- @memoized('uid', '_addressbookHomes')
- def addressbookHomeWithUID(self, uid, create=False):
- data = self.execSQL(
- "select RESOURCE_ID from ADDRESSBOOK_HOME where OWNER_UID = %s",
- [uid]
- )
- if not data:
- if not create:
- return None
- self.execSQL(
- "insert into ADDRESSBOOK_HOME (OWNER_UID) values (%s)",
- [uid]
- )
- home = self.addressbookHomeWithUID(uid)
- home.createAddressBookWithName("addressbook")
- return home
- resid = data[0][0]
-
- if self._notifierFactory:
- notifier = self._notifierFactory.newNotifier(id=uid)
- else:
- notifier = None
-
- return PostgresAddressBookHome(self, uid, resid, notifier)
-
-
- @memoized('uid', '_notificationHomes')
- def notificationsWithUID(self, uid):
- """
- Implement notificationsWithUID.
- """
- rows = self.execSQL(
- """
- select RESOURCE_ID from NOTIFICATION_HOME where
- OWNER_UID = %s
- """, [uid])
- if rows:
- [[resourceID]] = rows
- else:
- [[resourceID]] = self.execSQL("select nextval('RESOURCE_ID_SEQ')")
- resourceID = str(resourceID)
- self.execSQL(
- "insert into NOTIFICATION_HOME (RESOURCE_ID, OWNER_UID) "
- "values (%s, %s)", [resourceID, uid])
- return PostgresNotificationCollection(self, uid, resourceID)
-
-
- def abort(self):
- if not self._completed:
- # print 'ABORTING', self._label
- self._completed = True
- self._connection.rollback()
- self._connection.close()
- 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()
- else:
- raise AlreadyFinishedError()
-
-
- def postCommit(self, operation):
- """
- Run things after 'commit.'
- """
- self._postCommitOperations.append(operation)
- # FIXME: implement.
-
# CARDDAV
class PostgresAddressBookObject(object):
@@ -3130,6 +2925,9 @@
return names
+ def createdHome(self):
+ self.createAddressBookWithName("addressbook")
+
def addressbooks(self):
"""
Retrieve addressbooks contained in this addressbook home.
@@ -3258,26 +3056,3 @@
return self._notifier.getID(label)
else:
return None
-
-
-#
-
-
-class PostgresStore(Service, object):
-
- implements(IDataStore)
-
- def __init__(self, connectionFactory, notifierFactory, attachmentsPath):
- self.connectionFactory = connectionFactory
- self.notifierFactory = notifierFactory
- self.attachmentsPath = attachmentsPath
-
-
- def newTransaction(self, label="unlabeled"):
- return PostgresTransaction(
- self,
- self.connectionFactory(),
- self.notifierFactory,
- label
- )
-
Modified: CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/common.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/common.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/common.py 2010-08-23 18:36:43 UTC (rev 6168)
@@ -730,8 +730,8 @@
home.calendarWithName(calendar.name()))
self.assertEquals(
- list(c.name() for c in calendars),
- home1_calendarNames
+ set(c.name() for c in calendars),
+ set(home1_calendarNames)
)
Modified: CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/test_postgres.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/test_postgres.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txcaldav/calendarstore/test/test_postgres.py 2010-08-23 18:36:43 UTC (rev 6168)
@@ -24,11 +24,12 @@
from txcaldav.calendarstore.test.common import CommonTests as CalendarCommonTests
from txcarddav.addressbookstore.test.common import CommonTests as AddressBookCommonTests
from txdav.common.icommondatastore import NoSuchHomeChildError
+from txdav.common.datastore.sql import CommonDataStore
from twisted.trial import unittest
from txdav.datastore.subpostgres import (PostgresService,
DiagnosticConnectionWrapper)
-from txcaldav.calendarstore.postgres import PostgresStore, v1_schema
+from txcaldav.calendarstore.postgres import v1_schema
from twisted.internet.defer import Deferred, inlineCallbacks, succeed
from twisted.internet import reactor
from twext.python.filepath import CachingFilePath
@@ -80,7 +81,7 @@
except OSError:
pass
try:
- self.store = PostgresStore(
+ self.store = CommonDataStore(
lambda label=None: connectionFactory(
label or currentTestID
),
@@ -172,10 +173,11 @@
calendars = self.requirements[homeUID]
if calendars is not None:
home = populateTxn.calendarHomeWithUID(homeUID, True)
- # We don't want the default calendar to appear unless it's
+ # We don't want the default calendar or inbox to appear unless it's
# explicitly listed.
try:
home.removeCalendarWithName("calendar")
+ home.removeCalendarWithName("inbox")
except NoSuchHomeChildError:
pass
for calendarName in calendars:
Modified: CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/file.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/file.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txcarddav/addressbookstore/file.py 2010-08-23 18:36:43 UTC (rev 6168)
@@ -73,7 +73,7 @@
def _addressbookStore(self):
return self._dataStore
- def created(self):
+ def createdHome(self):
self.createAddressBookWithName("addressbook")
class AddressBook(CommonHomeChild):
Modified: CalendarServer/branches/generic-sqlstore/txdav/common/datastore/file.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/common/datastore/file.py 2010-08-23 15:48:59 UTC (rev 6167)
+++ CalendarServer/branches/generic-sqlstore/txdav/common/datastore/file.py 2010-08-23 18:36:43 UTC (rev 6168)
@@ -200,7 +200,7 @@
notifier)
self._homes[storeType][(uid, self)] = home
if creating:
- home.created()
+ home.createdHome()
# Create notification collection
if storeType == ECALENDARTYPE:
Added: CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py (rev 0)
+++ CalendarServer/branches/generic-sqlstore/txdav/common/datastore/sql.py 2010-08-23 18:36:43 UTC (rev 6168)
@@ -0,0 +1,214 @@
+# -*- 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.
+##
+from twext.python.log import Logger
+from txdav.datastore.sql import memoized
+from txcaldav.calendarstore.postgres import PostgresCalendarHome,\
+ PostgresAddressBookHome, PostgresNotificationCollection
+
+"""
+SQL data store.
+"""
+
+__all__ = [
+ "CommonDataStore",
+ "CommonStoreTransaction",
+]
+
+from twistedcaldav.sharing import SharedCollectionRecord #@UnusedImport
+
+from zope.interface.declarations import implements, directlyProvides
+
+from twisted.application.service import Service
+
+from txdav.idav import IDataStore, AlreadyFinishedError
+
+from txcaldav.icalendarstore import ICalendarTransaction
+from txcarddav.iaddressbookstore import IAddressBookTransaction
+
+
+log = Logger()
+
+ECALENDARTYPE = 0
+EADDRESSBOOKTYPE = 1
+
+class CommonDataStore(Service, object):
+
+ implements(IDataStore)
+
+ def __init__(self, connectionFactory, notifierFactory, attachmentsPath,
+ enableCalendars=True, enableAddressBooks=True):
+ assert enableCalendars or enableAddressBooks
+
+ self.connectionFactory = connectionFactory
+ self.notifierFactory = notifierFactory
+ self.attachmentsPath = attachmentsPath
+ self.enableCalendars = enableCalendars
+ self.enableAddressBooks = enableAddressBooks
+
+
+ def newTransaction(self, label="unlabeled"):
+ return CommonStoreTransaction(
+ self,
+ self.connectionFactory(),
+ self.enableCalendars,
+ self.enableAddressBooks,
+ self.notifierFactory,
+ label
+ )
+
+class CommonStoreTransaction(object):
+ """
+ Transaction implementation for SQL database.
+ """
+
+ _homeClass = {}
+
+ def __init__(self, store, connection, enableCalendars, enableAddressBooks, notifierFactory, label):
+
+ self._store = store
+ self._connection = connection
+ self._cursor = connection.cursor()
+ self._completed = False
+ self._calendarHomes = {}
+ self._addressbookHomes = {}
+ self._notificationHomes = {}
+ self._postCommitOperations = []
+ self._notifierFactory = notifierFactory
+ self._label = label
+
+ extraInterfaces = []
+ if enableCalendars:
+ extraInterfaces.append(ICalendarTransaction)
+ if enableAddressBooks:
+ extraInterfaces.append(IAddressBookTransaction)
+ directlyProvides(self, *extraInterfaces)
+
+ CommonStoreTransaction._homeClass[ECALENDARTYPE] = PostgresCalendarHome
+ CommonStoreTransaction._homeClass[EADDRESSBOOKTYPE] = PostgresAddressBookHome
+
+ def store(self):
+ return self._store
+
+
+ def __repr__(self):
+ return 'PG-TXN<%s>' % (self._label,)
+
+
+ def execSQL(self, sql, args=[]):
+ # print 'EXECUTE %s: %s' % (self._label, sql)
+ self._cursor.execute(sql, args)
+ if self._cursor.description:
+ return self._cursor.fetchall()
+ else:
+ return None
+
+
+ def __del__(self):
+ if not self._completed:
+ self._connection.rollback()
+ self._connection.close()
+
+
+ @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)
+
+ def homeWithUID(self, storeType, uid, create=False):
+
+ if storeType == ECALENDARTYPE:
+ homeTable = "CALENDAR_HOME"
+ elif storeType == EADDRESSBOOKTYPE:
+ homeTable = "ADDRESSBOOK_HOME"
+
+ data = self.execSQL(
+ "select RESOURCE_ID from %s where OWNER_UID = %%s" % (homeTable,),
+ [uid]
+ )
+ if not data:
+ if not create:
+ return None
+ self.execSQL(
+ "insert into %s (OWNER_UID) values (%%s)" % (homeTable,),
+ [uid]
+ )
+ home = self.homeWithUID(storeType, uid)
+ home.createdHome()
+ return home
+ resid = data[0][0]
+
+ if self._notifierFactory:
+ notifier = self._notifierFactory.newNotifier(id=uid)
+ else:
+ notifier = None
+
+ return self._homeClass[storeType](self, uid, resid, notifier)
+
+
+ @memoized('uid', '_notificationHomes')
+ def notificationsWithUID(self, uid):
+ """
+ Implement notificationsWithUID.
+ """
+ rows = self.execSQL(
+ """
+ select RESOURCE_ID from NOTIFICATION_HOME where
+ OWNER_UID = %s
+ """, [uid])
+ if rows:
+ [[resourceID]] = rows
+ else:
+ [[resourceID]] = self.execSQL("select nextval('RESOURCE_ID_SEQ')")
+ resourceID = str(resourceID)
+ self.execSQL(
+ "insert into NOTIFICATION_HOME (RESOURCE_ID, OWNER_UID) "
+ "values (%s, %s)", [resourceID, uid])
+ return PostgresNotificationCollection(self, uid, resourceID)
+
+
+ def abort(self):
+ if not self._completed:
+ # print 'ABORTING', self._label
+ self._completed = True
+ self._connection.rollback()
+ self._connection.close()
+ 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()
+ else:
+ raise AlreadyFinishedError()
+
+
+ def postCommit(self, operation):
+ """
+ Run things after 'commit.'
+ """
+ self._postCommitOperations.append(operation)
+ # FIXME: implement.
+
Added: CalendarServer/branches/generic-sqlstore/txdav/datastore/sql.py
===================================================================
--- CalendarServer/branches/generic-sqlstore/txdav/datastore/sql.py (rev 0)
+++ CalendarServer/branches/generic-sqlstore/txdav/datastore/sql.py 2010-08-23 18:36:43 UTC (rev 6168)
@@ -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
+
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100823/4664297e/attachment-0001.html>
More information about the calendarserver-changes
mailing list