← Back to team overview

launchpad-reviewers team mailing list archive

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