1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2013 OpenERP (<http://www.openerp.com>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 # decorator makes wrappers that have the same API as their wrapped function;
23 # this is important for the openerp.api.guess() that relies on signatures
24 from decorator import decorator
25 from inspect import getargspec
30 logger = logging.getLogger(__name__)
33 class ormcache(object):
34 """ LRU cache decorator for orm methods. """
36 def __init__(self, skiparg=2, size=8192, multi=None, timeout=None):
37 self.skiparg = skiparg
43 def __call__(self, method):
45 lookup = decorator(self.lookup, method)
46 lookup.clear_cache = self.clear
50 return "lookup-stats hit=%s miss=%s err=%s ratio=%.1f" % \
51 (self.stat_hit, self.stat_miss, self.stat_err,
52 (100*float(self.stat_hit))/(self.stat_miss+self.stat_hit))
55 ormcache = model._ormcache
57 d = ormcache[self.method]
59 d = ormcache[self.method] = lru.LRU(self.size)
62 def lookup(self, method, *args, **kwargs):
64 key = args[self.skiparg:]
71 value = d[key] = self.method(*args, **kwargs)
75 return self.method(*args, **kwargs)
77 def clear(self, model, *args):
78 """ Remove *args entry from the cache or all keys if *args is undefined """
81 logger.warn("ormcache.clear arguments are deprecated and ignored "
82 "(while clearing caches on (%s).%s)",
83 model._name, self.method.__name__)
85 model.pool._any_cache_cleared = True
88 class ormcache_context(ormcache):
89 def __init__(self, skiparg=2, size=8192, accepted_keys=()):
90 super(ormcache_context,self).__init__(skiparg,size)
91 self.accepted_keys = accepted_keys
93 def __call__(self, method):
94 # remember which argument is context
95 args = getargspec(method)[0]
96 self.context_pos = args.index('context')
97 return super(ormcache_context, self).__call__(method)
99 def lookup(self, method, *args, **kwargs):
100 d = self.lru(args[0])
102 # Note. The decorator() wrapper (used in __call__ above) will resolve
103 # arguments, and pass them positionally to lookup(). This is why context
104 # is not passed through kwargs!
105 if self.context_pos < len(args):
106 context = args[self.context_pos]
108 context = kwargs.get('context') or {}
109 ckey = [(k, context[k]) for k in self.accepted_keys if k in context]
111 # Beware: do not take the context from args!
112 key = args[self.skiparg:self.context_pos] + tuple(ckey)
119 value = d[key] = self.method(*args, **kwargs)
123 return self.method(*args, **kwargs)
126 class ormcache_multi(ormcache):
127 def __init__(self, skiparg=2, size=8192, multi=3):
128 assert skiparg <= multi
129 super(ormcache_multi, self).__init__(skiparg, size)
132 def lookup(self, method, *args, **kwargs):
133 d = self.lru(args[0])
134 base_key = args[self.skiparg:self.multi] + args[self.multi+1:]
135 ids = args[self.multi]
139 # first take what is available in the cache
141 key = base_key + (i,)
150 # call the method for the ids that were not in the cache
152 args[self.multi] = missed
153 result.update(method(*args, **kwargs))
155 # store those new results back in the cache
157 key = base_key + (i,)
163 class dummy_cache(object):
164 """ Cache decorator replacement to actually do no caching. """
165 def __init__(self, *l, **kw):
168 def __call__(self, fn):
169 fn.clear_cache = self.clear
172 def clear(self, *l, **kw):
176 # For backward compatibility
179 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: