diff options
-rw-r--r-- | README.rst | 4 | ||||
-rw-r--r-- | cachetools/__init__.py | 20 | ||||
-rw-r--r-- | cachetools/cache.py | 4 | ||||
-rw-r--r-- | cachetools/decorators.py | 14 | ||||
-rw-r--r-- | cachetools/fifo.py | 2 | ||||
-rw-r--r-- | cachetools/func.py | 12 | ||||
-rw-r--r-- | cachetools/keys.py | 2 | ||||
-rw-r--r-- | cachetools/lfu.py | 4 | ||||
-rw-r--r-- | cachetools/lru.py | 2 | ||||
-rw-r--r-- | cachetools/mru.py | 2 | ||||
-rw-r--r-- | cachetools/rr.py | 2 | ||||
-rw-r--r-- | cachetools/ttl.py | 5 | ||||
-rw-r--r-- | docs/conf.py | 18 | ||||
-rw-r--r-- | setup.cfg | 5 | ||||
-rw-r--r-- | tests/__init__.py | 10 | ||||
-rw-r--r-- | tests/test_func.py | 25 | ||||
-rw-r--r-- | tests/test_keys.py | 3 | ||||
-rw-r--r-- | tests/test_method.py | 11 | ||||
-rw-r--r-- | tests/test_mru.py | 4 | ||||
-rw-r--r-- | tests/test_wrapper.py | 8 | ||||
-rw-r--r-- | tox.ini | 2 |
21 files changed, 80 insertions, 79 deletions
@@ -25,6 +25,10 @@ cachetools :target: https://raw.github.com/tkem/cachetools/master/LICENSE :alt: License +.. image:: https://img.shields.io/badge/code%20style-black-000000.svg + :target: https://github.com/psf/black + :alt: Code style: black + This module provides various memoizing collections and decorators, including variants of the Python Standard Library's `@lru_cache`_ function decorator. diff --git a/cachetools/__init__.py b/cachetools/__init__.py index 2e66e20..1c925cc 100644 --- a/cachetools/__init__.py +++ b/cachetools/__init__.py @@ -10,15 +10,15 @@ from .rr import RRCache from .ttl import TTLCache __all__ = ( - 'Cache', - 'FIFOCache', - 'LFUCache', - 'LRUCache', - 'MRUCache', - 'RRCache', - 'TTLCache', - 'cached', - 'cachedmethod' + "Cache", + "FIFOCache", + "LFUCache", + "LRUCache", + "MRUCache", + "RRCache", + "TTLCache", + "cached", + "cachedmethod", ) -__version__ = '4.2.1' +__version__ = "4.2.1" diff --git a/cachetools/cache.py b/cachetools/cache.py index ed3d268..0c81d06 100644 --- a/cachetools/cache.py +++ b/cachetools/cache.py @@ -32,7 +32,7 @@ class Cache(MutableMapping): self.__maxsize = maxsize def __repr__(self): - return '%s(%r, maxsize=%r, currsize=%r)' % ( + return "%s(%r, maxsize=%r, currsize=%r)" % ( self.__class__.__name__, list(self.__data.items()), self.__maxsize, @@ -49,7 +49,7 @@ class Cache(MutableMapping): maxsize = self.__maxsize size = self.getsizeof(value) if size > maxsize: - raise ValueError('value too large') + raise ValueError("value too large") if key not in self.__data or self.__size[key] < size: while self.__currsize + size > maxsize: self.popitem() diff --git a/cachetools/decorators.py b/cachetools/decorators.py index 217b9a8..3e78603 100644 --- a/cachetools/decorators.py +++ b/cachetools/decorators.py @@ -8,11 +8,15 @@ def cached(cache, key=hashkey, lock=None): results in a cache. """ + def decorator(func): if cache is None: + def wrapper(*args, **kwargs): return func(*args, **kwargs) + elif lock is None: + def wrapper(*args, **kwargs): k = key(*args, **kwargs) try: @@ -25,7 +29,9 @@ def cached(cache, key=hashkey, lock=None): except ValueError: pass # value too large return v + else: + def wrapper(*args, **kwargs): k = key(*args, **kwargs) try: @@ -40,7 +46,9 @@ def cached(cache, key=hashkey, lock=None): return cache.setdefault(k, v) except ValueError: return v # value too large + return functools.update_wrapper(wrapper, func) + return decorator @@ -49,8 +57,10 @@ def cachedmethod(cache, key=hashkey, lock=None): callable that saves results in a cache. """ + def decorator(method): if lock is None: + def wrapper(self, *args, **kwargs): c = cache(self) if c is None: @@ -66,7 +76,9 @@ def cachedmethod(cache, key=hashkey, lock=None): except ValueError: pass # value too large return v + else: + def wrapper(self, *args, **kwargs): c = cache(self) if c is None: @@ -84,5 +96,7 @@ def cachedmethod(cache, key=hashkey, lock=None): return c.setdefault(k, v) except ValueError: return v # value too large + return functools.update_wrapper(wrapper, method) + return decorator diff --git a/cachetools/fifo.py b/cachetools/fifo.py index 9f254f1..e7c377e 100644 --- a/cachetools/fifo.py +++ b/cachetools/fifo.py @@ -26,6 +26,6 @@ class FIFOCache(Cache): try: key = next(iter(self.__order)) except StopIteration: - raise KeyError('%s is empty' % type(self).__name__) from None + raise KeyError("%s is empty" % type(self).__name__) from None else: return (key, self.pop(key)) diff --git a/cachetools/func.py b/cachetools/func.py index 2be517e..57fb72d 100644 --- a/cachetools/func.py +++ b/cachetools/func.py @@ -19,16 +19,15 @@ from .mru import MRUCache from .rr import RRCache from .ttl import TTLCache -__all__ = ('lfu_cache', 'lru_cache', 'mru_cache', 'rr_cache', 'ttl_cache') +__all__ = ("lfu_cache", "lru_cache", "mru_cache", "rr_cache", "ttl_cache") -_CacheInfo = collections.namedtuple('CacheInfo', [ - 'hits', 'misses', 'maxsize', 'currsize' -]) +_CacheInfo = collections.namedtuple( + "CacheInfo", ["hits", "misses", "maxsize", "currsize"] +) class _UnboundCache(dict): - @property def maxsize(self): return None @@ -88,9 +87,10 @@ def _cache(cache, typed): wrapper.cache_info = cache_info wrapper.cache_clear = cache_clear - wrapper.cache_parameters = lambda: {'maxsize': maxsize, 'typed': typed} + wrapper.cache_parameters = lambda: {"maxsize": maxsize, "typed": typed} functools.update_wrapper(wrapper, func) return wrapper + return decorator diff --git a/cachetools/keys.py b/cachetools/keys.py index 355d742..13630a4 100644 --- a/cachetools/keys.py +++ b/cachetools/keys.py @@ -1,6 +1,6 @@ """Key functions for memoizing decorators.""" -__all__ = ('hashkey', 'typedkey') +__all__ = ("hashkey", "typedkey") class _HashedTuple(tuple): diff --git a/cachetools/lfu.py b/cachetools/lfu.py index 894a326..6289b5c 100644 --- a/cachetools/lfu.py +++ b/cachetools/lfu.py @@ -27,8 +27,8 @@ class LFUCache(Cache): def popitem(self): """Remove and return the `(key, value)` pair least frequently used.""" try: - (key, _), = self.__counter.most_common(1) + ((key, _),) = self.__counter.most_common(1) except ValueError: - raise KeyError('%s is empty' % type(self).__name__) from None + raise KeyError("%s is empty" % type(self).__name__) from None else: return (key, self.pop(key)) diff --git a/cachetools/lru.py b/cachetools/lru.py index 33749d1..dbbe787 100644 --- a/cachetools/lru.py +++ b/cachetools/lru.py @@ -29,7 +29,7 @@ class LRUCache(Cache): try: key = next(iter(self.__order)) except StopIteration: - raise KeyError('%s is empty' % type(self).__name__) from None + raise KeyError("%s is empty" % type(self).__name__) from None else: return (key, self.pop(key)) diff --git a/cachetools/mru.py b/cachetools/mru.py index 41e2aa1..92ec6db 100644 --- a/cachetools/mru.py +++ b/cachetools/mru.py @@ -29,7 +29,7 @@ class MRUCache(Cache): try: key = next(iter(self.__order)) except StopIteration: - raise KeyError('%s is empty' % type(self).__name__) from None + raise KeyError("%s is empty" % type(self).__name__) from None else: return (key, self.pop(key)) diff --git a/cachetools/rr.py b/cachetools/rr.py index 5b47e87..561dbe5 100644 --- a/cachetools/rr.py +++ b/cachetools/rr.py @@ -29,6 +29,6 @@ class RRCache(Cache): try: key = self.__choice(list(self)) except IndexError: - raise KeyError('%s is empty' % type(self).__name__) from None + raise KeyError("%s is empty" % type(self).__name__) from None else: return (key, self.pop(key)) diff --git a/cachetools/ttl.py b/cachetools/ttl.py index 528a085..72f6d52 100644 --- a/cachetools/ttl.py +++ b/cachetools/ttl.py @@ -6,7 +6,7 @@ from .cache import Cache class _Link(object): - __slots__ = ('key', 'expire', 'next', 'prev') + __slots__ = ("key", "expire", "next", "prev") def __init__(self, key=None, expire=None): self.key = key @@ -23,7 +23,6 @@ class _Link(object): class _Timer(object): - def __init__(self, timer): self.__timer = timer self.__nesting = 0 @@ -198,7 +197,7 @@ class TTLCache(Cache): try: key = next(iter(self.__links)) except StopIteration: - raise KeyError('%s is empty' % type(self).__name__) from None + raise KeyError("%s is empty" % type(self).__name__) from None else: return (key, self.pop(key)) diff --git a/docs/conf.py b/docs/conf.py index 9efbd40..f55e5a5 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -8,17 +8,17 @@ def get_version(): return cp["metadata"]["version"] -project = 'cachetools' -copyright = '2014-2021 Thomas Kemmer' +project = "cachetools" +copyright = "2014-2021 Thomas Kemmer" version = get_version() release = version extensions = [ - 'sphinx.ext.autodoc', - 'sphinx.ext.coverage', - 'sphinx.ext.doctest', - 'sphinx.ext.todo' + "sphinx.ext.autodoc", + "sphinx.ext.coverage", + "sphinx.ext.doctest", + "sphinx.ext.todo", ] -exclude_patterns = ['_build'] -master_doc = 'index' -html_theme = 'default' +exclude_patterns = ["_build"] +master_doc = "index" +html_theme = "default" @@ -33,7 +33,10 @@ exclude = tests.* [flake8] -exclude = .git, .tox +max-line-length = 80 +exclude = .git, .tox, build +select = C, E, F, W, B, B950, I, N +ignore = E501, W503 [build_sphinx] source-dir = docs/ diff --git a/tests/__init__.py b/tests/__init__.py index 70a0f03..bfd5ec6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -12,7 +12,7 @@ class CacheTestMixin(object): self.assertEqual(1, cache.maxsize) self.assertEqual(0, cache.currsize) self.assertEqual(1, cache.getsizeof(None)) - self.assertEqual(1, cache.getsizeof('')) + self.assertEqual(1, cache.getsizeof("")) self.assertEqual(1, cache.getsizeof(0)) self.assertTrue(repr(cache).startswith(cache.__class__.__name__)) @@ -47,10 +47,10 @@ class CacheTestMixin(object): self.assertEqual(1, cache[1]) self.assertEqual(2, cache[2]) - cache.update({1: 'a', 2: 'b'}) + cache.update({1: "a", 2: "b"}) self.assertEqual(2, len(cache)) - self.assertEqual('a', cache[1]) - self.assertEqual('b', cache[2]) + self.assertEqual("a", cache[1]) + self.assertEqual("b", cache[2]) def test_delete(self): cache = self.Cache(maxsize=2) @@ -108,7 +108,7 @@ class CacheTestMixin(object): with self.assertRaises(KeyError): cache.popitem() - @unittest.skipUnless(sys.version_info >= (3, 7), 'requires Python 3.7') + @unittest.skipUnless(sys.version_info >= (3, 7), "requires Python 3.7") def test_popitem_exception_context(self): # since Python 3.7, MutableMapping.popitem() suppresses # exception context as implementation detail diff --git a/tests/test_func.py b/tests/test_func.py index 39dce77..721d5a6 100644 --- a/tests/test_func.py +++ b/tests/test_func.py @@ -4,15 +4,12 @@ import cachetools.func class DecoratorTestMixin(object): - def decorator(self, maxsize, **kwargs): return self.DECORATOR(maxsize, **kwargs) def test_decorator(self): cached = self.decorator(maxsize=2)(lambda n: n) - self.assertEqual(cached.cache_parameters(), { - 'maxsize': 2, 'typed': False - }) + self.assertEqual(cached.cache_parameters(), {"maxsize": 2, "typed": False}) self.assertEqual(cached.cache_info(), (0, 0, 2, 0)) self.assertEqual(cached(1), 1) self.assertEqual(cached.cache_info(), (0, 1, 2, 1)) @@ -23,9 +20,7 @@ class DecoratorTestMixin(object): def test_decorator_clear(self): cached = self.decorator(maxsize=2)(lambda n: n) - self.assertEqual(cached.cache_parameters(), { - 'maxsize': 2, 'typed': False - }) + self.assertEqual(cached.cache_parameters(), {"maxsize": 2, "typed": False}) self.assertEqual(cached.cache_info(), (0, 0, 2, 0)) self.assertEqual(cached(1), 1) self.assertEqual(cached.cache_info(), (0, 1, 2, 1)) @@ -36,9 +31,7 @@ class DecoratorTestMixin(object): def test_decorator_nocache(self): cached = self.decorator(maxsize=0)(lambda n: n) - self.assertEqual(cached.cache_parameters(), { - 'maxsize': 0, 'typed': False - }) + self.assertEqual(cached.cache_parameters(), {"maxsize": 0, "typed": False}) self.assertEqual(cached.cache_info(), (0, 0, 0, 0)) self.assertEqual(cached(1), 1) self.assertEqual(cached.cache_info(), (0, 1, 0, 0)) @@ -49,9 +42,7 @@ class DecoratorTestMixin(object): def test_decorator_unbound(self): cached = self.decorator(maxsize=None)(lambda n: n) - self.assertEqual(cached.cache_parameters(), { - 'maxsize': None, 'typed': False - }) + self.assertEqual(cached.cache_parameters(), {"maxsize": None, "typed": False}) self.assertEqual(cached.cache_info(), (0, 0, None, 0)) self.assertEqual(cached(1), 1) self.assertEqual(cached.cache_info(), (0, 1, None, 1)) @@ -62,9 +53,7 @@ class DecoratorTestMixin(object): def test_decorator_typed(self): cached = self.decorator(maxsize=2, typed=True)(lambda n: n) - self.assertEqual(cached.cache_parameters(), { - 'maxsize': 2, 'typed': True - }) + self.assertEqual(cached.cache_parameters(), {"maxsize": 2, "typed": True}) self.assertEqual(cached.cache_info(), (0, 0, 2, 0)) self.assertEqual(cached(1), 1) self.assertEqual(cached.cache_info(), (0, 1, 2, 1)) @@ -77,9 +66,7 @@ class DecoratorTestMixin(object): def test_decorator_user_function(self): cached = self.decorator(lambda n: n) - self.assertEqual(cached.cache_parameters(), { - 'maxsize': 128, 'typed': False - }) + self.assertEqual(cached.cache_parameters(), {"maxsize": 128, "typed": False}) self.assertEqual(cached.cache_info(), (0, 0, 128, 0)) self.assertEqual(cached(1), 1) self.assertEqual(cached.cache_info(), (0, 1, 128, 1)) diff --git a/tests/test_keys.py b/tests/test_keys.py index 8137704..892a620 100644 --- a/tests/test_keys.py +++ b/tests/test_keys.py @@ -4,7 +4,6 @@ import cachetools.keys class CacheKeysTest(unittest.TestCase): - def test_hashkey(self, key=cachetools.keys.hashkey): self.assertEqual(key(), key()) self.assertEqual(hash(key()), hash(key())) @@ -47,7 +46,7 @@ class CacheKeysTest(unittest.TestCase): def test_pickle(self, key=cachetools.keys.hashkey): import pickle - for k in [key(), key('abc'), key('abc', 123), key('abc', q='abc')]: + for k in [key(), key("abc"), key("abc", 123), key("abc", q="abc")]: # white-box test: assert cached hash value is not pickled self.assertEqual(len(k.__dict__), 0) h = hash(k) diff --git a/tests/test_method.py b/tests/test_method.py index 9252fef..235eca4 100644 --- a/tests/test_method.py +++ b/tests/test_method.py @@ -5,18 +5,17 @@ from cachetools import LRUCache, cachedmethod, keys class Cached(object): - def __init__(self, cache, count=0): self.cache = cache self.count = count - @cachedmethod(operator.attrgetter('cache')) + @cachedmethod(operator.attrgetter("cache")) def get(self, value): count = self.count self.count += 1 return count - @cachedmethod(operator.attrgetter('cache'), key=keys.typedkey) + @cachedmethod(operator.attrgetter("cache"), key=keys.typedkey) def get_typed(self, value): count = self.count self.count += 1 @@ -24,16 +23,15 @@ class Cached(object): # https://github.com/tkem/cachetools/issues/107 def __hash__(self): - raise TypeError('unhashable type') + raise TypeError("unhashable type") class Locked(object): - def __init__(self, cache): self.cache = cache self.count = 0 - @cachedmethod(operator.attrgetter('cache'), lock=lambda self: self) + @cachedmethod(operator.attrgetter("cache"), lock=lambda self: self) def get(self, value): return self.count @@ -45,7 +43,6 @@ class Locked(object): class CachedMethodTest(unittest.TestCase): - def test_dict(self): cached = Cached({}) diff --git a/tests/test_mru.py b/tests/test_mru.py index 3a9f4d3..d11dba4 100644 --- a/tests/test_mru.py +++ b/tests/test_mru.py @@ -17,7 +17,7 @@ class MRUCacheTest(unittest.TestCase, CacheTestMixin): cache[3] = 3 # Evicts 1 because nothing's been used yet assert len(cache) == 2 - assert 1 not in cache, 'Wrong key was evicted. Should have been `1`.' + assert 1 not in cache, "Wrong key was evicted. Should have been '1'." assert 2 in cache assert 3 in cache @@ -29,7 +29,7 @@ class MRUCacheTest(unittest.TestCase, CacheTestMixin): cache[1] cache[2] cache[3] = 3 # Evicts 2 - assert 2 not in cache, 'Wrong key was evicted. Should have been `2`.' + assert 2 not in cache, "Wrong key was evicted. Should have been '2'." assert 1 in cache assert 3 in cache diff --git a/tests/test_wrapper.py b/tests/test_wrapper.py index a6e649c..e154ff8 100644 --- a/tests/test_wrapper.py +++ b/tests/test_wrapper.py @@ -5,12 +5,11 @@ import cachetools.keys class DecoratorTestMixin(object): - def cache(self, minsize): raise NotImplementedError def func(self, *args, **kwargs): - if hasattr(self, 'count'): + if hasattr(self, "count"): self.count += 1 else: self.count = 0 @@ -101,7 +100,6 @@ class DecoratorTestMixin(object): class CacheWrapperTest(unittest.TestCase, DecoratorTestMixin): - def cache(self, minsize): return cachetools.Cache(maxsize=minsize) @@ -138,13 +136,11 @@ class CacheWrapperTest(unittest.TestCase, DecoratorTestMixin): class DictWrapperTest(unittest.TestCase, DecoratorTestMixin): - def cache(self, minsize): return dict() class NoneWrapperTest(unittest.TestCase): - def func(self, *args, **kwargs): return args + tuple(kwargs.items()) @@ -154,4 +150,4 @@ class NoneWrapperTest(unittest.TestCase): self.assertEqual(wrapper(0), (0,)) self.assertEqual(wrapper(1), (1,)) - self.assertEqual(wrapper(1, foo='bar'), (1, ('foo', 'bar'))) + self.assertEqual(wrapper(1, foo="bar"), (1, ("foo", "bar"))) @@ -31,6 +31,8 @@ commands = [testenv:flake8] deps = flake8 + flake8-black; python_version >= "3.6" and implementation_name == "cpython" + flake8-bugbear flake8-import-order commands = flake8 |