Source code for libutilitaspy.aspects.memoizer

"""
This module implements a general Memoizer aspect for memoizing (caching) method results.

@author Ernesto Posse
"""

from abc import ABCMeta

from core import Aspect, WeaverMetaClassFactory
from libutilitaspy.general.utils import make_hashable 

[docs]class Memoizer(Aspect): """ A memoizer aspect decorates methods with actions to remember previously computed values for the methods and the given arguments to avoid recomputing them. Warning: it is not thread-safe, therefore if a method is executed in multiple threads, the cached results may be inconsistent. """ def __init__(self): self.cache = {}
[docs] def before(self, klass, method, obj, *args, **kwargs): """Try to return the previously computed value for the method, the given object and given arguments, if it has already been computed and stored in the cache table. If it has not been computed, create a new entry in the cache table for the method, the given object and given arguments.""" hashable_args = make_hashable(args) hashable_kwargs = make_hashable(kwargs) all_args = (hashable_args, hashable_kwargs) try: return self.cache[method, obj][1][all_args] except KeyError: # If there is no previously cached value for the method and given arguments, # we store a pair (a, d) in the entry for (method, object) where a is the # current (hashable) arguments, and d is a dictionary mapping specific arguments # to return values. We store a temporarily until the execution of 'after' so that # the 'after' method knows which are the current arguments so that it can store # the return value in the appropriate table entry. if (method, obj) not in self.cache: self.cache[method, obj] = (all_args, { all_args: None }) else: args_to_val_map = self.cache[method, obj][1] args_to_val_map[all_args] = None self.cache[method, obj] = (all_args, args_to_val_map)
[docs] def after(self, klass, method, obj, retval, exc_type, exc_val, traceback): """Store the return value of the method in the cache table.""" current_args, args_to_val_map = self.cache[method, obj] args_to_val_map[current_args] = retval self.cache[method, obj] = (None, args_to_val_map)
MemoizerMetaClass = WeaverMetaClassFactory(Memoizer()) class MemoizerAbstractMetaClass(MemoizerMetaClass, ABCMeta): pass