Code review comment for lp:~allenap/launchpad/cache-experiment-roll-out

Revision history for this message
Gavin Panella (allenap) wrote :

While this branch appears very long, the majority of it is mechanical
changes. I've attached a diff of the non-mechanical parts.

1=== removed file 'lib/canonical/cachedproperty.py'
2--- lib/canonical/cachedproperty.py 2010-08-17 07:03:25 +0000
3+++ lib/canonical/cachedproperty.py 1970-01-01 00:00:00 +0000
4@@ -1,229 +0,0 @@
5-# Copyright 2009 Canonical Ltd. This software is licensed under the
6-# GNU Affero General Public License version 3 (see the file LICENSE).
7-
8-"""Cached properties for situations where a property is computed once and
9-then returned each time it is asked for.
10-
11-The clear_cachedproperties function can be used to wipe the cache of properties
12-from an instance.
13-"""
14-
15-__metaclass__ = type
16-
17-__all__ = [
18- 'cache_property',
19- 'cachedproperty',
20- 'clear_cachedproperties',
21- 'clear_property',
22- ]
23-
24-from zope.security.proxy import removeSecurityProxy
25-
26-from canonical.lazr.utils import safe_hasattr
27-
28-# XXX: JonathanLange 2010-01-11 bug=505731: Move this to lp.services.
29-
30-def cachedproperty(attrname_or_fn):
31- """A decorator for methods that makes them properties with their return
32- value cached.
33-
34- The value is cached on the instance, using the attribute name provided.
35-
36- If you don't provide a name, the mangled name of the property is used.
37-
38- cachedproperty is not threadsafe - it should not be used on objects which
39- are shared across threads / external locking should be used on those
40- objects.
41-
42- >>> class CachedPropertyTest(object):
43- ...
44- ... @cachedproperty('_foo_cache')
45- ... def foo(self):
46- ... print 'foo computed'
47- ... return 23
48- ...
49- ... @cachedproperty
50- ... def bar(self):
51- ... print 'bar computed'
52- ... return 69
53-
54- >>> cpt = CachedPropertyTest()
55- >>> getattr(cpt, '_foo_cache', None) is None
56- True
57- >>> cpt.foo
58- foo computed
59- 23
60- >>> cpt.foo
61- 23
62- >>> cpt._foo_cache
63- 23
64- >>> cpt.bar
65- bar computed
66- 69
67- >>> cpt._bar_cached_value
68- 69
69-
70- Cached properties are listed on instances.
71- >>> sorted(cpt._cached_properties)
72- ['_bar_cached_value', '_foo_cache']
73-
74- """
75- if isinstance(attrname_or_fn, basestring):
76- attrname = attrname_or_fn
77- return CachedPropertyForAttr(attrname)
78- else:
79- fn = attrname_or_fn
80- attrname = '_%s_cached_value' % fn.__name__
81- return CachedProperty(attrname, fn)
82-
83-def cache_property(instance, attrname, value):
84- """Cache value on instance as attrname.
85-
86- instance._cached_properties is updated with attrname.
87-
88- >>> class CachedPropertyTest(object):
89- ...
90- ... @cachedproperty('_foo_cache')
91- ... def foo(self):
92- ... return 23
93- ...
94- >>> instance = CachedPropertyTest()
95- >>> cache_property(instance, '_foo_cache', 42)
96- >>> instance.foo
97- 42
98- >>> instance._cached_properties
99- ['_foo_cache']
100-
101- Caching a new value does not duplicate the cache keys.
102-
103- >>> cache_property(instance, '_foo_cache', 84)
104- >>> instance._cached_properties
105- ['_foo_cache']
106-
107- And does update the cached value.
108-
109- >>> instance.foo
110- 84
111- """
112- naked_instance = removeSecurityProxy(instance)
113- clear_property(naked_instance, attrname)
114- setattr(naked_instance, attrname, value)
115- cached_properties = getattr(naked_instance, '_cached_properties', [])
116- cached_properties.append(attrname)
117- naked_instance._cached_properties = cached_properties
118-
119-
120-def clear_property(instance, attrname):
121- """Remove a cached attribute from instance.
122-
123- The attribute name is removed from instance._cached_properties.
124-
125- If the property is not cached, nothing happens.
126-
127- :seealso clear_cachedproperties: For clearing all cached items at once.
128-
129- >>> class CachedPropertyTest(object):
130- ...
131- ... @cachedproperty('_foo_cache')
132- ... def foo(self):
133- ... return 23
134- ...
135- >>> instance = CachedPropertyTest()
136- >>> instance.foo
137- 23
138- >>> clear_property(instance, '_foo_cache')
139- >>> instance._cached_properties
140- []
141- >>> is_cached(instance, '_foo_cache')
142- False
143- >>> clear_property(instance, '_foo_cache')
144- """
145- naked_instance = removeSecurityProxy(instance)
146- if not is_cached(naked_instance, attrname):
147- return
148- delattr(naked_instance, attrname)
149- naked_instance._cached_properties.remove(attrname)
150-
151-
152-def clear_cachedproperties(instance):
153- """Clear cached properties from an object.
154-
155- >>> class CachedPropertyTest(object):
156- ...
157- ... @cachedproperty('_foo_cache')
158- ... def foo(self):
159- ... return 23
160- ...
161- >>> instance = CachedPropertyTest()
162- >>> instance.foo
163- 23
164- >>> instance._cached_properties
165- ['_foo_cache']
166- >>> clear_cachedproperties(instance)
167- >>> instance._cached_properties
168- []
169- >>> hasattr(instance, '_foo_cache')
170- False
171- """
172- naked_instance = removeSecurityProxy(instance)
173- cached_properties = getattr(naked_instance, '_cached_properties', [])
174- for property_name in cached_properties:
175- delattr(naked_instance, property_name)
176- naked_instance._cached_properties = []
177-
178-
179-def is_cached(instance, attrname):
180- """Return True if attrname is cached on instance.
181-
182- >>> class CachedPropertyTest(object):
183- ...
184- ... @cachedproperty('_foo_cache')
185- ... def foo(self):
186- ... return 23
187- ...
188- >>> instance = CachedPropertyTest()
189- >>> instance.foo
190- 23
191- >>> is_cached(instance, '_foo_cache')
192- True
193- >>> is_cached(instance, '_var_cache')
194- False
195- """
196- naked_instance = removeSecurityProxy(instance)
197- return safe_hasattr(naked_instance, attrname)
198-
199-
200-class CachedPropertyForAttr:
201- """Curry a decorator to provide arguments to the CachedProperty."""
202-
203- def __init__(self, attrname):
204- self.attrname = attrname
205-
206- def __call__(self, fn):
207- return CachedProperty(self.attrname, fn)
208-
209-
210-class CachedProperty:
211-
212- # Used to detect not-yet-cached properties.
213- sentinel = object()
214-
215- def __init__(self, attrname, fn):
216- self.fn = fn
217- self.attrname = attrname
218-
219- def __get__(self, inst, cls=None):
220- if inst is None:
221- return self
222- cachedresult = getattr(inst, self.attrname, CachedProperty.sentinel)
223- if cachedresult is CachedProperty.sentinel:
224- result = self.fn(inst)
225- cache_property(inst, self.attrname, result)
226- return result
227- else:
228- return cachedresult
229-
230-
231-if __name__ == '__main__':
232- import doctest
233- doctest.testmod()
234
235=== modified file 'lib/canonical/database/sqlbase.py'
236--- lib/canonical/database/sqlbase.py 2010-08-23 10:57:10 +0000
237+++ lib/canonical/database/sqlbase.py 2010-08-24 10:29:11 +0000
238@@ -65,7 +65,6 @@
239 from zope.interface import implements
240 from zope.security.proxy import removeSecurityProxy
241
242-from canonical.cachedproperty import clear_cachedproperties
243 from canonical.config import (
244 config,
245 dbconfig,
246@@ -271,10 +270,6 @@
247 # XXX: RobertCollins 2010-08-16 bug=622648: Note this is not directly
248 # tested, but the entire test suite blows up awesomely if it's broken.
249 # It's entirely unclear where tests for this should be.
250-
251- # While canonical.cachedproperty and lp.services.propertycache are
252- # both in use, we must clear the caches for both.
253- clear_cachedproperties(self)
254 IPropertyCacheManager(self).clear()
255
256
257
258=== removed file 'lib/canonical/tests/test_cachedproperty.py'
259--- lib/canonical/tests/test_cachedproperty.py 2010-07-14 14:11:15 +0000
260+++ lib/canonical/tests/test_cachedproperty.py 1970-01-01 00:00:00 +0000
261@@ -1,15 +0,0 @@
262-# Copyright 2009 Canonical Ltd. This software is licensed under the
263-# GNU Affero General Public License version 3 (see the file LICENSE).
264-
265-from doctest import DocTestSuite, ELLIPSIS
266-import unittest
267-
268-import canonical.cachedproperty
269-
270-def test_suite():
271- suite = DocTestSuite(canonical.cachedproperty, optionflags=ELLIPSIS)
272- return suite
273-
274-
275-if __name__ == '__main__':
276- unittest.main(defaultTest='test_suite')
277
278=== modified file 'lib/lp/bugs/model/bug.py'
279--- lib/lp/bugs/model/bug.py 2010-08-22 18:31:30 +0000
280+++ lib/lp/bugs/model/bug.py 2010-08-24 10:12:39 +0000
281@@ -70,9 +70,9 @@
282 providedBy,
283 )
284
285-from canonical.cachedproperty import (
286+from lp.services.propertycache import (
287 cachedproperty,
288- clear_property,
289+ IPropertyCache,
290 )
291 from canonical.config import config
292 from canonical.database.constants import UTC_NOW
293@@ -633,7 +633,7 @@
294 # disabled see the change.
295 store.flush()
296 self.updateHeat()
297- clear_property(self, '_cached_viewers')
298+ del IPropertyCache(self)._known_viewers
299 return
300
301 def unsubscribeFromDupes(self, person, unsubscribed_by):
302@@ -1626,7 +1626,7 @@
303 self, self.messages[comment_number])
304 bug_message.visible = visible
305
306- @cachedproperty('_cached_viewers')
307+ @cachedproperty
308 def _known_viewers(self):
309 """A dict of of known persons able to view this bug."""
310 return set()
311
312=== modified file 'lib/lp/bugs/model/bugtask.py'
313--- lib/lp/bugs/model/bugtask.py 2010-08-22 18:31:30 +0000
314+++ lib/lp/bugs/model/bugtask.py 2010-08-24 10:32:39 +0000
315@@ -59,7 +59,6 @@
316 removeSecurityProxy,
317 )
318
319-from canonical.cachedproperty import cache_property
320 from canonical.config import config
321 from canonical.database.constants import UTC_NOW
322 from canonical.database.datetimecol import UtcDateTimeCol
323@@ -156,6 +155,9 @@
324 from lp.soyuz.interfaces.publishing import PackagePublishingStatus
325 from lp.soyuz.model.publishing import SourcePackagePublishingHistory
326 from lp.soyuz.model.sourcepackagerelease import SourcePackageRelease
327+from lp.services.propertycache import (
328+ IPropertyCache,
329+ )
330
331
332 debbugsseveritymap = {None: BugTaskImportance.UNDECIDED,
333@@ -1324,7 +1326,7 @@
334 """
335 userid = user.id
336 def cache_user_can_view_bug(bugtask):
337- cache_property(bugtask.bug, '_cached_viewers', set([userid]))
338+ IPropertyCache(bugtask.bug)._known_viewers = set([userid])
339 return bugtask
340 return cache_user_can_view_bug
341
342
343=== modified file 'lib/lp/registry/browser/person.py'
344--- lib/lp/registry/browser/person.py 2010-08-23 03:25:20 +0000
345+++ lib/lp/registry/browser/person.py 2010-08-24 10:19:26 +0000
346@@ -141,7 +141,10 @@
347 from zope.security.interfaces import Unauthorized
348 from zope.security.proxy import removeSecurityProxy
349
350-from canonical.cachedproperty import cachedproperty
351+from lp.services.propertycache import (
352+ cachedproperty,
353+ IPropertyCache,
354+ )
355 from canonical.config import config
356 from canonical.database.sqlbase import flush_database_updates
357 from canonical.launchpad import (
358@@ -5693,10 +5696,7 @@
359 def _reset_state(self):
360 """Reset the cache because the recipients changed."""
361 self._count_recipients = None
362- if safe_hasattr(self, '_all_recipients_cached'):
363- # The clear the cache of _all_recipients. The caching will fail
364- # if this method creates the attribute before _all_recipients.
365- del self._all_recipients_cached
366+ del IPropertyCache(self)._all_recipients
367
368 def _getPrimaryReason(self, person_or_team):
369 """Return the primary reason enumeration.
370@@ -5804,7 +5804,7 @@
371 'You are contacting %s of the %s (%s) team directly.'
372 % (text, person_or_team.displayname, person_or_team.name))
373
374- @cachedproperty('_all_recipients_cached')
375+ @cachedproperty
376 def _all_recipients(self):
377 """Set the cache of all recipients."""
378 all_recipients = {}
379
380=== modified file 'lib/lp/registry/model/distroseries.py'
381--- lib/lp/registry/model/distroseries.py 2010-08-22 18:31:30 +0000
382+++ lib/lp/registry/model/distroseries.py 2010-08-24 10:20:53 +0000
383@@ -547,7 +547,7 @@
384 orderBy=["Language.englishname"])
385 return result
386
387- @cachedproperty('_previous_series_cached')
388+ @cachedproperty
389 def previous_series(self):
390 """See `IDistroSeries`."""
391 # This property is cached because it is used intensely inside
392
393=== modified file 'lib/lp/registry/model/product.py'
394--- lib/lp/registry/model/product.py 2010-08-22 19:26:46 +0000
395+++ lib/lp/registry/model/product.py 2010-08-24 10:23:04 +0000
396@@ -38,7 +38,10 @@
397 from zope.interface import implements
398 from zope.security.proxy import removeSecurityProxy
399
400-from canonical.cachedproperty import cachedproperty
401+from lp.services.propertycache import (
402+ cachedproperty,
403+ IPropertyCache,
404+ )
405 from canonical.database.constants import UTC_NOW
406 from canonical.database.datetimecol import UtcDateTimeCol
407 from canonical.database.enumcol import EnumCol
408@@ -428,7 +431,7 @@
409 notNull=True, default=False,
410 storm_validator=_validate_license_approved)
411
412- @cachedproperty('_commercial_subscription_cached')
413+ @cachedproperty
414 def commercial_subscription(self):
415 return CommercialSubscription.selectOneBy(product=self)
416
417@@ -475,7 +478,7 @@
418 purchaser=purchaser,
419 sales_system_id=voucher,
420 whiteboard=whiteboard)
421- self._commercial_subscription_cached = subscription
422+ IPropertyCache(self).commercial_subscription = subscription
423 else:
424 if current_datetime <= self.commercial_subscription.date_expires:
425 # Extend current subscription.
426
427=== modified file 'lib/lp/soyuz/model/distroseriesbinarypackage.py'
428--- lib/lp/soyuz/model/distroseriesbinarypackage.py 2010-08-20 20:31:18 +0000
429+++ lib/lp/soyuz/model/distroseriesbinarypackage.py 2010-08-24 10:25:36 +0000
430@@ -12,7 +12,10 @@
431 from storm.store import Store
432 from zope.interface import implements
433
434-from canonical.cachedproperty import cachedproperty
435+from lp.services.propertycache import (
436+ cachedproperty,
437+ IPropertyCache,
438+ )
439 from canonical.database.sqlbase import sqlvalues
440 from lp.soyuz.interfaces.distroseriesbinarypackage import (
441 IDistroSeriesBinaryPackage,
442@@ -40,7 +43,7 @@
443 self.distroseries = distroseries
444 self.binarypackagename = binarypackagename
445 if cache is not None:
446- self._cache = cache
447+ IPropertyCache(self).cache = cache
448
449 @property
450 def name(self):
451@@ -58,7 +61,7 @@
452 """See IDistroSeriesBinaryPackage."""
453 return self.distroseries.distribution
454
455- @cachedproperty('_cache')
456+ @cachedproperty
457 def cache(self):
458 """See IDistroSeriesBinaryPackage."""
459 store = Store.of(self.distroseries)
460
461=== modified file 'lib/lp/testopenid/browser/server.py'
462--- lib/lp/testopenid/browser/server.py 2010-08-20 20:31:18 +0000
463+++ lib/lp/testopenid/browser/server.py 2010-08-24 10:27:59 +0000
464@@ -32,7 +32,10 @@
465 from zope.security.proxy import isinstance as zisinstance
466 from zope.session.interfaces import ISession
467
468-from canonical.cachedproperty import cachedproperty
469+from lp.services.propertycache import (
470+ cachedproperty,
471+ IPropertyCache,
472+ )
473 from canonical.launchpad import _
474 from canonical.launchpad.interfaces.account import (
475 AccountStatus,
476@@ -142,7 +145,7 @@
477 return (self.openid_request.idSelect() or
478 self.openid_request.identity == self.user_identity_url)
479
480- @cachedproperty('_openid_parameters')
481+ @cachedproperty
482 def openid_parameters(self):
483 """A dictionary of OpenID query parameters from request."""
484 query = {}
485@@ -165,8 +168,9 @@
486 def restoreRequestFromSession(self):
487 """Get the OpenIDRequest from our session."""
488 session = self.getSession()
489+ cache = IPropertyCache(self)
490 try:
491- self._openid_parameters = session[OPENID_REQUEST_SESSION_KEY]
492+ cache.openid_parameters = session[OPENID_REQUEST_SESSION_KEY]
493 except KeyError:
494 raise UnexpectedFormData("No OpenID request in session")
495
496
497=== modified file 'lib/lp/shipit.py'
498--- lib/lp/shipit.py 2010-08-20 20:31:18 +0000
499+++ lib/lp/shipit.py 2010-08-24 15:27:46 +0000
500@@ -106,6 +106,10 @@
501 from lp.registry.model.person import Person
502 from lp.services.mail import stub
503 from lp.services.mail.sendmail import simple_sendmail
504+from lp.services.propertycache import (
505+ cachedproperty,
506+ IPropertyCache,
507+ )
508 from lp.services.scripts.base import (
509 LaunchpadCronScript,
510 LaunchpadScript,

« Back to merge proposal