import operator import unittest from cachetools import LRUCache, cachedmethod, keys class Cached: def __init__(self, cache, count=0): self.cache = cache self.count = count @cachedmethod(operator.attrgetter("cache")) def get(self, value): count = self.count self.count += 1 return count @cachedmethod(operator.attrgetter("cache"), key=keys.typedkey) def get_typed(self, value): count = self.count self.count += 1 return count # https://github.com/tkem/cachetools/issues/107 def __hash__(self): raise TypeError("unhashable type") class Locked: def __init__(self, cache): self.cache = cache self.count = 0 @cachedmethod(operator.attrgetter("cache"), lock=lambda self: self) def get(self, value): return self.count def __enter__(self): self.count += 1 def __exit__(self, *exc): pass class CachedMethodTest(unittest.TestCase): def test_dict(self): cached = Cached({}) self.assertEqual(cached.get(0), 0) self.assertEqual(cached.get(1), 1) self.assertEqual(cached.get(1), 1) self.assertEqual(cached.get(1.0), 1) self.assertEqual(cached.get(1.0), 1) cached.cache.clear() self.assertEqual(cached.get(1), 2) def test_typed_dict(self): cached = Cached(LRUCache(maxsize=2)) self.assertEqual(cached.get_typed(0), 0) self.assertEqual(cached.get_typed(1), 1) self.assertEqual(cached.get_typed(1), 1) self.assertEqual(cached.get_typed(1.0), 2) self.assertEqual(cached.get_typed(1.0), 2) self.assertEqual(cached.get_typed(0.0), 3) self.assertEqual(cached.get_typed(0), 4) def test_lru(self): cached = Cached(LRUCache(maxsize=2)) self.assertEqual(cached.get(0), 0) self.assertEqual(cached.get(1), 1) self.assertEqual(cached.get(1), 1) self.assertEqual(cached.get(1.0), 1) self.assertEqual(cached.get(1.0), 1) cached.cache.clear() self.assertEqual(cached.get(1), 2) def test_typed_lru(self): cached = Cached(LRUCache(maxsize=2)) self.assertEqual(cached.get_typed(0), 0) self.assertEqual(cached.get_typed(1), 1) self.assertEqual(cached.get_typed(1), 1) self.assertEqual(cached.get_typed(1.0), 2) self.assertEqual(cached.get_typed(1.0), 2) self.assertEqual(cached.get_typed(0.0), 3) self.assertEqual(cached.get_typed(0), 4) def test_nospace(self): cached = Cached(LRUCache(maxsize=0)) self.assertEqual(cached.get(0), 0) self.assertEqual(cached.get(1), 1) self.assertEqual(cached.get(1), 2) self.assertEqual(cached.get(1.0), 3) self.assertEqual(cached.get(1.0), 4) def test_nocache(self): cached = Cached(None) self.assertEqual(cached.get(0), 0) self.assertEqual(cached.get(1), 1) self.assertEqual(cached.get(1), 2) self.assertEqual(cached.get(1.0), 3) self.assertEqual(cached.get(1.0), 4) def test_weakref(self): import weakref import fractions import gc # in Python 3.4, `int` does not support weak references even # when subclassed, but Fraction apparently does... class Int(fractions.Fraction): def __add__(self, other): return Int(fractions.Fraction.__add__(self, other)) cached = Cached(weakref.WeakValueDictionary(), count=Int(0)) self.assertEqual(cached.get(0), 0) gc.collect() self.assertEqual(cached.get(0), 1) ref = cached.get(1) self.assertEqual(ref, 2) self.assertEqual(cached.get(1), 2) self.assertEqual(cached.get(1.0), 2) ref = cached.get_typed(1) self.assertEqual(ref, 3) self.assertEqual(cached.get_typed(1), 3) self.assertEqual(cached.get_typed(1.0), 4) cached.cache.clear() self.assertEqual(cached.get(1), 5) def test_locked_dict(self): cached = Locked({}) self.assertEqual(cached.get(0), 1) self.assertEqual(cached.get(1), 3) self.assertEqual(cached.get(1), 3) self.assertEqual(cached.get(1.0), 3) self.assertEqual(cached.get(2.0), 7) def test_locked_nocache(self): cached = Locked(None) self.assertEqual(cached.get(0), 0) self.assertEqual(cached.get(1), 0) self.assertEqual(cached.get(1), 0) self.assertEqual(cached.get(1.0), 0) self.assertEqual(cached.get(1.0), 0) def test_locked_nospace(self): cached = Locked(LRUCache(maxsize=0)) self.assertEqual(cached.get(0), 1) self.assertEqual(cached.get(1), 3) self.assertEqual(cached.get(1), 5) self.assertEqual(cached.get(1.0), 7) self.assertEqual(cached.get(1.0), 9)