Revision: 6482 http://trac.macosforge.org/projects/calendarserver/changeset/6482 Author: wsanchez@apple.com Date: 2010-10-28 13:39:22 -0700 (Thu, 28 Oct 2010) Log Message: ----------- Rename memoized to memoizedKey and move it to twext. Modified Paths: -------------- CalendarServer/trunk/txdav/common/datastore/sql.py Added Paths: ----------- CalendarServer/trunk/twext/internet/decorate.py Removed Paths: ------------- CalendarServer/trunk/txdav/base/datastore/sql.py Added: CalendarServer/trunk/twext/internet/decorate.py =================================================================== --- CalendarServer/trunk/twext/internet/decorate.py (rev 0) +++ CalendarServer/trunk/twext/internet/decorate.py 2010-10-28 20:39:22 UTC (rev 6482) @@ -0,0 +1,114 @@ +## +# 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. +## + +""" +Decorators. +""" + +__all__ = [ + "memoizedKey", +] + + +from inspect import getargspec + +from twisted.internet.defer import Deferred, succeed + + +def memoizedKey(keyArgument, memoAttribute, deferredResult=True): + """ + 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} + + @param deferredResult: Whether the result must be a deferred. + @type keyArgument: C{bool} + """ + + 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 decorate(thunk): + # cheater move to try to get the right argspec from inlineCallbacks. + # This could probably be more robust, but the 'cell_contents' thing + # probably can't (that's the only real reference to the underlying + # function). + if thunk.func_code.co_name == "unwindGenerator": + specTarget = thunk.func_closure[0].cell_contents + else: + specTarget = thunk + spec = getargspec(specTarget) + + def outer(*a, **kw): + self = a[0] + memo = getattr(self, memoAttribute) + key = getarg(keyArgument, spec, a, kw) + if key in memo: + memoed = memo[key] + if deferredResult: + return succeed(memoed) + else: + return memoed + result = thunk(*a, **kw) + + if isinstance(result, Deferred): + def memoResult(finalResult): + if finalResult is not None: + memo[key] = finalResult + return finalResult + result.addCallback(memoResult) + elif result is not None: + memo[key] = result + + return result + + return outer + + return decorate Deleted: CalendarServer/trunk/txdav/base/datastore/sql.py =================================================================== --- CalendarServer/trunk/txdav/base/datastore/sql.py 2010-10-28 20:35:50 UTC (rev 6481) +++ CalendarServer/trunk/txdav/base/datastore/sql.py 2010-10-28 20:39:22 UTC (rev 6482) @@ -1,101 +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. -## - -""" -Logic common to SQL implementations. -""" - -from twisted.internet.defer import Deferred, succeed -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, deferredResult=True): - """ - 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} - - @param deferredResult: Whether the result must be a deferred. - @type keyArgument: C{bool} - - """ - def decorate(thunk): - # cheater move to try to get the right argspec from inlineCallbacks. - # This could probably be more robust, but the 'cell_contents' thing - # probably can't (that's the only real reference to the underlying - # function). - if thunk.func_code.co_name == 'unwindGenerator': - specTarget = thunk.func_closure[0].cell_contents - else: - specTarget = thunk - spec = getargspec(specTarget) - def outer(*a, **kw): - self = a[0] - memo = getattr(self, memoAttribute) - key = _getarg(keyArgument, spec, a, kw) - if key in memo: - memoed = memo[key] - return succeed(memoed) if deferredResult else memoed - result = thunk(*a, **kw) - if isinstance(result, Deferred): - def memoResult(finalResult): - if finalResult is not None: - memo[key] = finalResult - return finalResult - result.addCallback(memoResult) - elif result is not None: - memo[key] = result - return result - return outer - return decorate Modified: CalendarServer/trunk/txdav/common/datastore/sql.py =================================================================== --- CalendarServer/trunk/txdav/common/datastore/sql.py 2010-10-28 20:35:50 UTC (rev 6481) +++ CalendarServer/trunk/txdav/common/datastore/sql.py 2010-10-28 20:39:22 UTC (rev 6482) @@ -26,25 +26,26 @@ ] import sys - +import datetime from Queue import Queue -from twext.python.log import Logger, LoggingMixIn -from twext.web2.dav.element.rfc2518 import ResourceType -from twext.web2.http_headers import MimeType +from zope.interface.declarations import implements, directlyProvides -from twisted.application.service import Service from twisted.python import hashlib from twisted.python.modules import getModule from twisted.python.util import FancyEqMixin +from twisted.python.failure import Failure from twisted.internet import reactor from twisted.internet.defer import inlineCallbacks, returnValue, Deferred -from twisted.python.failure import Failure -from twistedcaldav.customxml import NotificationType -from twistedcaldav.dateops import datetimeMktime +from twisted.application.service import Service +from twext.python.log import Logger, LoggingMixIn +from twext.internet.decorate import memoizedKey +from twext.web2.dav.element.rfc2518 import ResourceType +from twext.web2.http_headers import MimeType + from txdav.common.datastore.sql_legacy import PostgresLegacyNotificationsEmulator from txdav.caldav.icalendarstore import ICalendarTransaction, ICalendarStore @@ -59,18 +60,16 @@ NoSuchObjectResourceError from txdav.common.inotifications import INotificationCollection, \ INotificationObject -from txdav.base.datastore.sql import memoized from txdav.idav import AlreadyFinishedError from txdav.base.propertystore.base import PropertyName from txdav.base.propertystore.sql import PropertyStore -from zope.interface.declarations import implements, directlyProvides +from twistedcaldav.customxml import NotificationType +from twistedcaldav.dateops import datetimeMktime -import datetime -v1_schema = getModule(__name__).filePath.sibling( - "sql_schema_v1.sql").getContent() +v1_schema = getModule(__name__).filePath.sibling("sql_schema_v1.sql").getContent() log = Logger() @@ -127,9 +126,9 @@ _DONE = object() -_STATE_STOPPED = 'STOPPED' -_STATE_RUNNING = 'RUNNING' -_STATE_STOPPING = 'STOPPING' +_STATE_STOPPED = "STOPPED" +_STATE_RUNNING = "RUNNING" +_STATE_STOPPING = "STOPPING" class ThreadHolder(object): """ @@ -261,7 +260,7 @@ def __repr__(self): - return 'PG-TXN<%s>' % (self._label,) + return "PG-TXN<%s>" % (self._label,) def _reallyExecSQL(self, sql, args=[], raiseOnZeroRowCount=None): @@ -295,16 +294,16 @@ def __del__(self): if not self._completed: - print 'CommonStoreTransaction.__del__: OK' + print "CommonStoreTransaction.__del__: OK" self.abort() - @memoized('uid', '_calendarHomes') + @memoizedKey("uid", "_calendarHomes") def calendarHomeWithUID(self, uid, create=False): return self.homeWithUID(ECALENDARTYPE, uid, create=create) - @memoized('uid', '_addressbookHomes') + @memoizedKey("uid", "_addressbookHomes") def addressbookHomeWithUID(self, uid, create=False): return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create) @@ -360,7 +359,7 @@ returnValue(homeObject) - @memoized('uid', '_notificationHomes') + @memoizedKey("uid", "_notificationHomes") @inlineCallbacks def notificationsWithUID(self, uid): """ @@ -419,7 +418,7 @@ def postCommit(self, operation): """ - Run things after 'commit.' + Run things after C{commit}. """ self._postCommitOperations.append(operation) @@ -539,7 +538,7 @@ returnValue(names) - @memoized('name', '_children') + @memoizedKey("name", "_children") def childWithName(self, name): """ Retrieve the child with the given C{name} contained in this @@ -551,7 +550,7 @@ return self._childWithName(name, owned=True) - @memoized('name', '_sharedChildren') + @memoizedKey("name", "_sharedChildren") def sharedChildWithName(self, name): """ Retrieve the shared child with the given C{name} contained in this @@ -851,7 +850,7 @@ Common ancestor class of AddressBooks and Calendars. """ - compareAttributes = '_name _home _resourceID'.split() + compareAttributes = "_name _home _resourceID".split() _objectResourceClass = None _bindTable = None @@ -963,6 +962,7 @@ def objectResourceWithUID(self, uid): return self._makeObjectResource(None, uid) + @inlineCallbacks def _makeObjectResource(self, name, uid): """ @@ -1338,7 +1338,7 @@ @type _path: L{FilePath} """ - compareAttributes = '_name _parentCollection'.split() + compareAttributes = "_name _parentCollection".split() _objectTable = None @@ -1504,7 +1504,7 @@ implements(INotificationCollection) - compareAttributes = '_uid _resourceID'.split() + compareAttributes = "_uid _resourceID".split() _objectResourceClass = None _revisionsTable = NOTIFICATION_OBJECT_REVISIONS_TABLE @@ -1536,7 +1536,7 @@ return "<%s: %s>" % (self.__class__.__name__, self._resourceID) def name(self): - return 'notification' + return "notification" def uid(self): return self._uid @@ -1571,7 +1571,7 @@ return self.notificationObjectWithUID(self._nameToUID(name)) - @memoized('uid', '_notifications') + @memoizedKey("uid", "_notifications") @inlineCallbacks def notificationObjectWithUID(self, uid): """ @@ -1759,7 +1759,7 @@ implements(INotificationObject) - compareAttributes = '_resourceID _home'.split() + compareAttributes = "_resourceID _home".split() def __init__(self, home, uid): self._home = home