launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #00735
Review diff
While this branch appears very long, the majority of it is mechanical
changes. I've attached a diff of the non-mechanical parts.
--
https://code.launchpad.net/~allenap/launchpad/cache-experiment-roll-out/+merge/33542
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~allenap/launchpad/cache-experiment-roll-out into lp:launchpad/devel.
=== removed file 'lib/canonical/cachedproperty.py'
--- lib/canonical/cachedproperty.py 2010-08-17 07:03:25 +0000
+++ lib/canonical/cachedproperty.py 1970-01-01 00:00:00 +0000
@@ -1,229 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Cached properties for situations where a property is computed once and
-then returned each time it is asked for.
-
-The clear_cachedproperties function can be used to wipe the cache of properties
-from an instance.
-"""
-
-__metaclass__ = type
-
-__all__ = [
- 'cache_property',
- 'cachedproperty',
- 'clear_cachedproperties',
- 'clear_property',
- ]
-
-from zope.security.proxy import removeSecurityProxy
-
-from canonical.lazr.utils import safe_hasattr
-
-# XXX: JonathanLange 2010-01-11 bug=505731: Move this to lp.services.
-
-def cachedproperty(attrname_or_fn):
- """A decorator for methods that makes them properties with their return
- value cached.
-
- The value is cached on the instance, using the attribute name provided.
-
- If you don't provide a name, the mangled name of the property is used.
-
- cachedproperty is not threadsafe - it should not be used on objects which
- are shared across threads / external locking should be used on those
- objects.
-
- >>> class CachedPropertyTest(object):
- ...
- ... @cachedproperty('_foo_cache')
- ... def foo(self):
- ... print 'foo computed'
- ... return 23
- ...
- ... @cachedproperty
- ... def bar(self):
- ... print 'bar computed'
- ... return 69
-
- >>> cpt = CachedPropertyTest()
- >>> getattr(cpt, '_foo_cache', None) is None
- True
- >>> cpt.foo
- foo computed
- 23
- >>> cpt.foo
- 23
- >>> cpt._foo_cache
- 23
- >>> cpt.bar
- bar computed
- 69
- >>> cpt._bar_cached_value
- 69
-
- Cached properties are listed on instances.
- >>> sorted(cpt._cached_properties)
- ['_bar_cached_value', '_foo_cache']
-
- """
- if isinstance(attrname_or_fn, basestring):
- attrname = attrname_or_fn
- return CachedPropertyForAttr(attrname)
- else:
- fn = attrname_or_fn
- attrname = '_%s_cached_value' % fn.__name__
- return CachedProperty(attrname, fn)
-
-def cache_property(instance, attrname, value):
- """Cache value on instance as attrname.
-
- instance._cached_properties is updated with attrname.
-
- >>> class CachedPropertyTest(object):
- ...
- ... @cachedproperty('_foo_cache')
- ... def foo(self):
- ... return 23
- ...
- >>> instance = CachedPropertyTest()
- >>> cache_property(instance, '_foo_cache', 42)
- >>> instance.foo
- 42
- >>> instance._cached_properties
- ['_foo_cache']
-
- Caching a new value does not duplicate the cache keys.
-
- >>> cache_property(instance, '_foo_cache', 84)
- >>> instance._cached_properties
- ['_foo_cache']
-
- And does update the cached value.
-
- >>> instance.foo
- 84
- """
- naked_instance = removeSecurityProxy(instance)
- clear_property(naked_instance, attrname)
- setattr(naked_instance, attrname, value)
- cached_properties = getattr(naked_instance, '_cached_properties', [])
- cached_properties.append(attrname)
- naked_instance._cached_properties = cached_properties
-
-
-def clear_property(instance, attrname):
- """Remove a cached attribute from instance.
-
- The attribute name is removed from instance._cached_properties.
-
- If the property is not cached, nothing happens.
-
- :seealso clear_cachedproperties: For clearing all cached items at once.
-
- >>> class CachedPropertyTest(object):
- ...
- ... @cachedproperty('_foo_cache')
- ... def foo(self):
- ... return 23
- ...
- >>> instance = CachedPropertyTest()
- >>> instance.foo
- 23
- >>> clear_property(instance, '_foo_cache')
- >>> instance._cached_properties
- []
- >>> is_cached(instance, '_foo_cache')
- False
- >>> clear_property(instance, '_foo_cache')
- """
- naked_instance = removeSecurityProxy(instance)
- if not is_cached(naked_instance, attrname):
- return
- delattr(naked_instance, attrname)
- naked_instance._cached_properties.remove(attrname)
-
-
-def clear_cachedproperties(instance):
- """Clear cached properties from an object.
-
- >>> class CachedPropertyTest(object):
- ...
- ... @cachedproperty('_foo_cache')
- ... def foo(self):
- ... return 23
- ...
- >>> instance = CachedPropertyTest()
- >>> instance.foo
- 23
- >>> instance._cached_properties
- ['_foo_cache']
- >>> clear_cachedproperties(instance)
- >>> instance._cached_properties
- []
- >>> hasattr(instance, '_foo_cache')
- False
- """
- naked_instance = removeSecurityProxy(instance)
- cached_properties = getattr(naked_instance, '_cached_properties', [])
- for property_name in cached_properties:
- delattr(naked_instance, property_name)
- naked_instance._cached_properties = []
-
-
-def is_cached(instance, attrname):
- """Return True if attrname is cached on instance.
-
- >>> class CachedPropertyTest(object):
- ...
- ... @cachedproperty('_foo_cache')
- ... def foo(self):
- ... return 23
- ...
- >>> instance = CachedPropertyTest()
- >>> instance.foo
- 23
- >>> is_cached(instance, '_foo_cache')
- True
- >>> is_cached(instance, '_var_cache')
- False
- """
- naked_instance = removeSecurityProxy(instance)
- return safe_hasattr(naked_instance, attrname)
-
-
-class CachedPropertyForAttr:
- """Curry a decorator to provide arguments to the CachedProperty."""
-
- def __init__(self, attrname):
- self.attrname = attrname
-
- def __call__(self, fn):
- return CachedProperty(self.attrname, fn)
-
-
-class CachedProperty:
-
- # Used to detect not-yet-cached properties.
- sentinel = object()
-
- def __init__(self, attrname, fn):
- self.fn = fn
- self.attrname = attrname
-
- def __get__(self, inst, cls=None):
- if inst is None:
- return self
- cachedresult = getattr(inst, self.attrname, CachedProperty.sentinel)
- if cachedresult is CachedProperty.sentinel:
- result = self.fn(inst)
- cache_property(inst, self.attrname, result)
- return result
- else:
- return cachedresult
-
-
-if __name__ == '__main__':
- import doctest
- doctest.testmod()
=== modified file 'lib/canonical/database/sqlbase.py'
--- lib/canonical/database/sqlbase.py 2010-08-23 10:57:10 +0000
+++ lib/canonical/database/sqlbase.py 2010-08-24 10:29:11 +0000
@@ -65,7 +65,6 @@
from zope.interface import implements
from zope.security.proxy import removeSecurityProxy
-from canonical.cachedproperty import clear_cachedproperties
from canonical.config import (
config,
dbconfig,
@@ -271,10 +270,6 @@
# XXX: RobertCollins 2010-08-16 bug=622648: Note this is not directly
# tested, but the entire test suite blows up awesomely if it's broken.
# It's entirely unclear where tests for this should be.
-
- # While canonical.cachedproperty and lp.services.propertycache are
- # both in use, we must clear the caches for both.
- clear_cachedproperties(self)
IPropertyCacheManager(self).clear()
=== removed file 'lib/canonical/tests/test_cachedproperty.py'
--- lib/canonical/tests/test_cachedproperty.py 2010-07-14 14:11:15 +0000
+++ lib/canonical/tests/test_cachedproperty.py 1970-01-01 00:00:00 +0000
@@ -1,15 +0,0 @@
-# Copyright 2009 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-from doctest import DocTestSuite, ELLIPSIS
-import unittest
-
-import canonical.cachedproperty
-
-def test_suite():
- suite = DocTestSuite(canonical.cachedproperty, optionflags=ELLIPSIS)
- return suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='test_suite')
=== modified file 'lib/lp/bugs/model/bug.py'
--- lib/lp/bugs/model/bug.py 2010-08-22 18:31:30 +0000
+++ lib/lp/bugs/model/bug.py 2010-08-24 10:12:39 +0000
@@ -70,9 +70,9 @@
providedBy,
)
-from canonical.cachedproperty import (
+from lp.services.propertycache import (
cachedproperty,
- clear_property,
+ IPropertyCache,
)
from canonical.config import config
from canonical.database.constants import UTC_NOW
@@ -633,7 +633,7 @@
# disabled see the change.
store.flush()
self.updateHeat()
- clear_property(self, '_cached_viewers')
+ del IPropertyCache(self)._known_viewers
return
def unsubscribeFromDupes(self, person, unsubscribed_by):
@@ -1626,7 +1626,7 @@
self, self.messages[comment_number])
bug_message.visible = visible
- @cachedproperty('_cached_viewers')
+ @cachedproperty
def _known_viewers(self):
"""A dict of of known persons able to view this bug."""
return set()
=== modified file 'lib/lp/bugs/model/bugtask.py'
--- lib/lp/bugs/model/bugtask.py 2010-08-22 18:31:30 +0000
+++ lib/lp/bugs/model/bugtask.py 2010-08-24 10:32:39 +0000
@@ -59,7 +59,6 @@
removeSecurityProxy,
)
-from canonical.cachedproperty import cache_property
from canonical.config import config
from canonical.database.constants import UTC_NOW
from canonical.database.datetimecol import UtcDateTimeCol
@@ -156,6 +155,9 @@
from lp.soyuz.interfaces.publishing import PackagePublishingStatus
from lp.soyuz.model.publishing import SourcePackagePublishingHistory
from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
+from lp.services.propertycache import (
+ IPropertyCache,
+ )
debbugsseveritymap = {None: BugTaskImportance.UNDECIDED,
@@ -1324,7 +1326,7 @@
"""
userid = user.id
def cache_user_can_view_bug(bugtask):
- cache_property(bugtask.bug, '_cached_viewers', set([userid]))
+ IPropertyCache(bugtask.bug)._known_viewers = set([userid])
return bugtask
return cache_user_can_view_bug
=== modified file 'lib/lp/registry/browser/person.py'
--- lib/lp/registry/browser/person.py 2010-08-23 03:25:20 +0000
+++ lib/lp/registry/browser/person.py 2010-08-24 10:19:26 +0000
@@ -141,7 +141,10 @@
from zope.security.interfaces import Unauthorized
from zope.security.proxy import removeSecurityProxy
-from canonical.cachedproperty import cachedproperty
+from lp.services.propertycache import (
+ cachedproperty,
+ IPropertyCache,
+ )
from canonical.config import config
from canonical.database.sqlbase import flush_database_updates
from canonical.launchpad import (
@@ -5693,10 +5696,7 @@
def _reset_state(self):
"""Reset the cache because the recipients changed."""
self._count_recipients = None
- if safe_hasattr(self, '_all_recipients_cached'):
- # The clear the cache of _all_recipients. The caching will fail
- # if this method creates the attribute before _all_recipients.
- del self._all_recipients_cached
+ del IPropertyCache(self)._all_recipients
def _getPrimaryReason(self, person_or_team):
"""Return the primary reason enumeration.
@@ -5804,7 +5804,7 @@
'You are contacting %s of the %s (%s) team directly.'
% (text, person_or_team.displayname, person_or_team.name))
- @cachedproperty('_all_recipients_cached')
+ @cachedproperty
def _all_recipients(self):
"""Set the cache of all recipients."""
all_recipients = {}
=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2010-08-22 18:31:30 +0000
+++ lib/lp/registry/model/distroseries.py 2010-08-24 10:20:53 +0000
@@ -547,7 +547,7 @@
orderBy=["Language.englishname"])
return result
- @cachedproperty('_previous_series_cached')
+ @cachedproperty
def previous_series(self):
"""See `IDistroSeries`."""
# This property is cached because it is used intensely inside
=== modified file 'lib/lp/registry/model/product.py'
--- lib/lp/registry/model/product.py 2010-08-22 19:26:46 +0000
+++ lib/lp/registry/model/product.py 2010-08-24 10:23:04 +0000
@@ -38,7 +38,10 @@
from zope.interface import implements
from zope.security.proxy import removeSecurityProxy
-from canonical.cachedproperty import cachedproperty
+from lp.services.propertycache import (
+ cachedproperty,
+ IPropertyCache,
+ )
from canonical.database.constants import UTC_NOW
from canonical.database.datetimecol import UtcDateTimeCol
from canonical.database.enumcol import EnumCol
@@ -428,7 +431,7 @@
notNull=True, default=False,
storm_validator=_validate_license_approved)
- @cachedproperty('_commercial_subscription_cached')
+ @cachedproperty
def commercial_subscription(self):
return CommercialSubscription.selectOneBy(product=self)
@@ -475,7 +478,7 @@
purchaser=purchaser,
sales_system_id=voucher,
whiteboard=whiteboard)
- self._commercial_subscription_cached = subscription
+ IPropertyCache(self).commercial_subscription = subscription
else:
if current_datetime <= self.commercial_subscription.date_expires:
# Extend current subscription.
=== modified file 'lib/lp/soyuz/model/distroseriesbinarypackage.py'
--- lib/lp/soyuz/model/distroseriesbinarypackage.py 2010-08-20 20:31:18 +0000
+++ lib/lp/soyuz/model/distroseriesbinarypackage.py 2010-08-24 10:25:36 +0000
@@ -12,7 +12,10 @@
from storm.store import Store
from zope.interface import implements
-from canonical.cachedproperty import cachedproperty
+from lp.services.propertycache import (
+ cachedproperty,
+ IPropertyCache,
+ )
from canonical.database.sqlbase import sqlvalues
from lp.soyuz.interfaces.distroseriesbinarypackage import (
IDistroSeriesBinaryPackage,
@@ -40,7 +43,7 @@
self.distroseries = distroseries
self.binarypackagename = binarypackagename
if cache is not None:
- self._cache = cache
+ IPropertyCache(self).cache = cache
@property
def name(self):
@@ -58,7 +61,7 @@
"""See IDistroSeriesBinaryPackage."""
return self.distroseries.distribution
- @cachedproperty('_cache')
+ @cachedproperty
def cache(self):
"""See IDistroSeriesBinaryPackage."""
store = Store.of(self.distroseries)
=== modified file 'lib/lp/testopenid/browser/server.py'
--- lib/lp/testopenid/browser/server.py 2010-08-20 20:31:18 +0000
+++ lib/lp/testopenid/browser/server.py 2010-08-24 10:27:59 +0000
@@ -32,7 +32,10 @@
from zope.security.proxy import isinstance as zisinstance
from zope.session.interfaces import ISession
-from canonical.cachedproperty import cachedproperty
+from lp.services.propertycache import (
+ cachedproperty,
+ IPropertyCache,
+ )
from canonical.launchpad import _
from canonical.launchpad.interfaces.account import (
AccountStatus,
@@ -142,7 +145,7 @@
return (self.openid_request.idSelect() or
self.openid_request.identity == self.user_identity_url)
- @cachedproperty('_openid_parameters')
+ @cachedproperty
def openid_parameters(self):
"""A dictionary of OpenID query parameters from request."""
query = {}
@@ -165,8 +168,9 @@
def restoreRequestFromSession(self):
"""Get the OpenIDRequest from our session."""
session = self.getSession()
+ cache = IPropertyCache(self)
try:
- self._openid_parameters = session[OPENID_REQUEST_SESSION_KEY]
+ cache.openid_parameters = session[OPENID_REQUEST_SESSION_KEY]
except KeyError:
raise UnexpectedFormData("No OpenID request in session")
=== modified file 'lib/lp/shipit.py'
--- lib/lp/shipit.py 2010-08-20 20:31:18 +0000
+++ lib/lp/shipit.py 2010-08-24 15:27:46 +0000
@@ -106,6 +106,10 @@
from lp.registry.model.person import Person
from lp.services.mail import stub
from lp.services.mail.sendmail import simple_sendmail
+from lp.services.propertycache import (
+ cachedproperty,
+ IPropertyCache,
+ )
from lp.services.scripts.base import (
LaunchpadCronScript,
LaunchpadScript,
References