diff options
author | Thomas Kemmer <tkemmer@computer.org> | 2020-12-09 21:47:03 +0100 |
---|---|---|
committer | Thomas Kemmer <tkemmer@computer.org> | 2020-12-09 23:38:17 +0100 |
commit | 986d815af6d8ed7c9f93404036dcee58ebf67765 (patch) | |
tree | 27f43358d4b8fae4900e06e5b1f552a9329738bd | |
parent | ca648b68fc40fe2a9eef8b469d0d130a03611f40 (diff) | |
download | cachetools-986d815af6d8ed7c9f93404036dcee58ebf67765.tar.gz |
Add FIFO cache implementation.
-rw-r--r-- | cachetools/__init__.py | 2 | ||||
-rw-r--r-- | cachetools/fifo.py | 32 | ||||
-rw-r--r-- | cachetools/func.py | 15 | ||||
-rw-r--r-- | docs/index.rst | 13 | ||||
-rw-r--r-- | tests/test_fifo.py | 57 | ||||
-rw-r--r-- | tests/test_func.py | 5 |
6 files changed, 124 insertions, 0 deletions
diff --git a/cachetools/__init__.py b/cachetools/__init__.py index 029b1a6..428ffc2 100644 --- a/cachetools/__init__.py +++ b/cachetools/__init__.py @@ -2,6 +2,7 @@ from .cache import Cache from .decorators import cached, cachedmethod +from .fifo import FIFOCache from .lfu import LFUCache from .lru import LRUCache from .mru import MRUCache @@ -10,6 +11,7 @@ from .ttl import TTLCache __all__ = ( 'Cache', + 'FIFOCache', 'LFUCache', 'LRUCache', 'MRUCache', diff --git a/cachetools/fifo.py b/cachetools/fifo.py new file mode 100644 index 0000000..38ddca1 --- /dev/null +++ b/cachetools/fifo.py @@ -0,0 +1,32 @@ +import collections + +from .cache import Cache + + +class FIFOCache(Cache): + """First In First Out (FIFO) cache implementation.""" + + def __init__(self, maxsize, getsizeof=None): + Cache.__init__(self, maxsize, getsizeof) + self.__order = collections.OrderedDict() + + def __setitem__(self, key, value, cache_setitem=Cache.__setitem__): + cache_setitem(self, key, value) + try: + self.__order.move_to_end(key) + except KeyError: + self.__order[key] = None + + def __delitem__(self, key, cache_delitem=Cache.__delitem__): + cache_delitem(self, key) + del self.__order[key] + + def popitem(self): + """Remove and return the `(key, value)` pair first inserted.""" + try: + key = next(iter(self.__order)) + except StopIteration: + msg = '%s is empty' % self.__class__.__name__ + raise KeyError(msg) from None + else: + return (key, self.pop(key)) diff --git a/cachetools/func.py b/cachetools/func.py index 0815bac..2be517e 100644 --- a/cachetools/func.py +++ b/cachetools/func.py @@ -12,6 +12,7 @@ except ImportError: # pragma: no cover from dummy_threading import RLock from . import keys +from .fifo import FIFOCache from .lfu import LFUCache from .lru import LRUCache from .mru import MRUCache @@ -93,6 +94,20 @@ def _cache(cache, typed): return decorator +def fifo_cache(maxsize=128, typed=False): + """Decorator to wrap a function with a memoizing callable that saves + up to `maxsize` results based on a First In First Out (FIFO) + algorithm. + + """ + if maxsize is None: + return _cache(_UnboundCache(), typed) + elif callable(maxsize): + return _cache(FIFOCache(128), typed)(maxsize) + else: + return _cache(FIFOCache(maxsize), typed) + + def lfu_cache(maxsize=128, typed=False): """Decorator to wrap a function with a memoizing callable that saves up to `maxsize` results based on a Least Frequently Used (LFU) diff --git a/docs/index.rst b/docs/index.rst index 08374ed..945bd78 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -76,6 +76,12 @@ computed when the item is inserted into the cache. additionally need to override :meth:`__getitem__`, :meth:`__setitem__` and :meth:`__delitem__`. +.. autoclass:: FIFOCache(maxsize, getsizeof=None) + :members: + + This class evicts items in the order they were added to make space + when necessary. + .. autoclass:: LFUCache(maxsize, getsizeof=None) :members: @@ -470,6 +476,13 @@ performance and clear the cache. Please see the all the decorators in this module are thread-safe by default. +.. decorator:: fifo_cache(user_function) + fifo_cache(maxsize=128, typed=False) + + Decorator that wraps a function with a memoizing callable that + saves up to `maxsize` results based on a First In First Out + (FIFO) algorithm. + .. decorator:: lfu_cache(user_function) lfu_cache(maxsize=128, typed=False) diff --git a/tests/test_fifo.py b/tests/test_fifo.py new file mode 100644 index 0000000..933af56 --- /dev/null +++ b/tests/test_fifo.py @@ -0,0 +1,57 @@ +import unittest + +from cachetools import FIFOCache + +from . import CacheTestMixin + + +class LRUCacheTest(unittest.TestCase, CacheTestMixin): + + Cache = FIFOCache + + def test_fifo(self): + cache = FIFOCache(maxsize=2) + + cache[1] = 1 + cache[2] = 2 + cache[3] = 3 + + self.assertEqual(len(cache), 2) + self.assertEqual(cache[2], 2) + self.assertEqual(cache[3], 3) + self.assertNotIn(1, cache) + + cache[2] + cache[4] = 4 + self.assertEqual(len(cache), 2) + self.assertEqual(cache[3], 3) + self.assertEqual(cache[4], 4) + self.assertNotIn(2, cache) + + cache[5] = 5 + self.assertEqual(len(cache), 2) + self.assertEqual(cache[4], 4) + self.assertEqual(cache[5], 5) + self.assertNotIn(3, cache) + + def test_fifo_getsizeof(self): + cache = FIFOCache(maxsize=3, getsizeof=lambda x: x) + + cache[1] = 1 + cache[2] = 2 + + self.assertEqual(len(cache), 2) + self.assertEqual(cache[1], 1) + self.assertEqual(cache[2], 2) + + cache[3] = 3 + + self.assertEqual(len(cache), 1) + self.assertEqual(cache[3], 3) + self.assertNotIn(1, cache) + self.assertNotIn(2, cache) + + with self.assertRaises(ValueError): + cache[4] = 4 + self.assertEqual(len(cache), 1) + self.assertEqual(cache[3], 3) diff --git a/tests/test_func.py b/tests/test_func.py index 2aebbd0..b6c4fe1 100644 --- a/tests/test_func.py +++ b/tests/test_func.py @@ -89,6 +89,11 @@ class DecoratorTestMixin(object): self.assertEqual(cached.cache_info(), (2, 1, 128, 1)) +class FIFODecoratorTest(unittest.TestCase, DecoratorTestMixin): + + DECORATOR = staticmethod(cachetools.func.fifo_cache) + + class LFUDecoratorTest(unittest.TestCase, DecoratorTestMixin): DECORATOR = staticmethod(cachetools.func.lfu_cache) |