Package trac ::
Module cache
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2009 Edgewall Software
4 # All rights reserved.
5 #
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://trac.edgewall.com/license.html.
9 #
10 # This software consists of voluntary contributions made by many
11 # individuals. For the exact contribution history, see the revision
12 # history and logs, available at http://trac.edgewall.org/.
13
14 from trac .core import Component
15 from trac .util .concurrency import ThreadLocal , threading
16
17 __all__ = ["CacheManager", "cached"]
21 """Cached property descriptor"""
22
23 - def __init__ (self, retriever, id_attr=None):
24 self.retriever = retriever
25 self.__doc__ = retriever.__doc__
26 self.id_attr = id_attr
27 self.id = None
28
29 - def __get__ (self, instance, owner):
30 if instance is None:
31 return self
32 if self.id_attr is not None:
33 id = getattr(instance, self.id_attr)
34 else:
35 id = self.id
36 if id is None:
37 id = self.id = self.make_id(owner)
38 return CacheManager (instance.env ).get (id , self.retriever, instance)
39
41 if self.id_attr is not None:
42 id = getattr(instance, self.id_attr)
43 else:
44 id = self.id
45 if id is None:
46 id = self.id = self.make_id(instance.__class__)
47 CacheManager (instance.env ).invalidate (id )
48
50 attr = self.retriever.__name__
51 for base in cls.mro():
52 if base.__dict__.get (attr) is self:
53 cls = base
54 break
55 return '%s.%s.%s' % (cls.__module__, cls.__name__, attr)
56
57
58 - def cached (fn_or_id=None):
59 """Method decorator creating a cached attribute from a data retrieval
60 method.
61
62 Accessing the cached attribute gives back the cached value. The data
63 retrieval method is called as needed by the CacheManager. Invalidating
64 the cache for this value is done by `del`eting the attribute.
65
66 The data retrieval method is called with a single argument `db` containing
67 a reference to a database connection. All data retrieval should be done
68 through this connection.
69
70 Note that the cache validity is maintained using a table in the database.
71 Cache invalidation is performed within a transaction block, and can be
72 nested within another transaction block.
73
74 The id used to identify the attribute in the database is constructed from
75 the names of the containing module, class and retriever method. If the
76 decorator is used in non-signleton (typically non-`Component`) objects,
77 an optional string specifying the name of the attribute containing the id
78 must be passed to the decorator call as follows:
79 {{{
80 def __init__(self, env, name):
81 self.env = env
82 self._metadata_id = 'custom_id.' + name
83
84 @cached('_metadata_id')
85 def metadata(db):
86 ...
87 }}}
88
89 This decorator requires that the object on which it is used has an `env`
90 attribute containing the application `Environment`.
91 """
92 if not hasattr(fn_or_id, '__call__'):
93 def decorator(fn):
94 return CachedProperty(fn, fn_or_id)
95 return decorator
96 else:
97 return CachedProperty(fn_or_id)