Browse Source

Fix lrucache decorator argspecs

tags/nilmdb-0.1
Jim Paris 8 years ago
parent
commit
b7f746e66d
2 changed files with 35 additions and 27 deletions
  1. +20
    -16
      nilmdb/utils/lrucache.py
  2. +15
    -11
      tests/test_lrucache.py

+ 20
- 16
nilmdb/utils/lrucache.py View File

@@ -4,12 +4,10 @@
# with added 'destructor' functionality.

import collections
import functools
import decorator
import warnings

def lru_cache(size = 10,
onremove = None,
keys = slice(None)):
def lru_cache(size = 10, onremove = None, keys = slice(None)):
"""Least-recently-used cache decorator.

@lru_cache(size = 10, onevict = None)
@@ -28,22 +26,23 @@ def lru_cache(size = 10,
f.cache_hits and f.cache_misses give statistics.
"""

def decorator(func):
def decorate(func):
cache = collections.OrderedDict() # order: least- to most-recent

def evict(value):
if onremove:
onremove(value)

@functools.wraps(func)
def wrapper(*args):
def wrapper(orig, *args, **kwargs):
if kwargs:
raise NotImplementedError("kwargs not supported")
key = args[keys]
try:
value = cache.pop(key)
wrapper.cache_hits += 1
orig.cache_hits += 1
except KeyError:
value = func(*args)
wrapper.cache_misses += 1
value = orig(*args)
orig.cache_misses += 1
if len(cache) >= size:
evict(cache.popitem(0)[1]) # evict LRU cache entry
cache[key] = value # (re-)insert this key at end
@@ -64,10 +63,15 @@ def lru_cache(size = 10,
for key in cache:
evict(cache.pop(key))

wrapper.cache_hits = 0
wrapper.cache_misses = 0
wrapper.cache_remove = cache_remove
wrapper.cache_remove_all = cache_remove_all
def cache_info():
return (func.cache_hits, func.cache_misses)

return wrapper
return decorator
new = decorator.decorator(wrapper, func)
func.cache_hits = 0
func.cache_misses = 0
new.cache_info = cache_info
new.cache_remove = cache_remove
new.cache_remove_all = cache_remove_all
return new

return decorate

+ 15
- 11
tests/test_lrucache.py View File

@@ -29,32 +29,32 @@ class Foo:
def __init__(self):
self.calls = 0
@nilmdb.utils.lru_cache(size = 3, keys = slice(1, 2))
def foo(self, n):
def foo(self, n, **kwargs):
self.calls += 1

class TestLRUCache(object):
def test(self):

[ foo1(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
eq_((foo1.cache_hits, foo1.cache_misses), (6, 3))
eq_(foo1.cache_info(), (6, 3))
[ foo1(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
eq_((foo1.cache_hits, foo1.cache_misses), (15, 3))
eq_(foo1.cache_info(), (15, 3))
[ foo1(n) for n in [ 4, 2, 1, 1, 4 ] ]
eq_((foo1.cache_hits, foo1.cache_misses), (18, 5))
eq_(foo1.cache_info(), (18, 5))

[ foo2(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
eq_((foo2.cache_hits, foo2.cache_misses), (6, 3))
eq_(foo2.cache_info(), (6, 3))
[ foo2(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
eq_((foo2.cache_hits, foo2.cache_misses), (15, 3))
eq_(foo2.cache_info(), (15, 3))
[ foo2(n) for n in [ 4, 2, 1, 1, 4 ] ]
eq_((foo2.cache_hits, foo2.cache_misses), (19, 4))
eq_(foo2.cache_info(), (19, 4))

[ foo3(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
eq_((foo3.cache_hits, foo3.cache_misses), (6, 3))
eq_(foo3.cache_info(), (6, 3))
[ foo3(n) for n in [ 1, 2, 3, 1, 2, 3, 1, 2, 3 ] ]
eq_((foo3.cache_hits, foo3.cache_misses), (15, 3))
eq_(foo3.cache_info(), (15, 3))
[ foo3(n) for n in [ 4, 2, 1, 1, 4 ] ]
eq_((foo3.cache_hits, foo3.cache_misses), (18, 5))
eq_(foo3.cache_info(), (18, 5))
eq_(foo3d.destructed, [1, 3])
with assert_raises(KeyError):
foo3.cache_remove(1,2,3)
@@ -70,10 +70,14 @@ class TestLRUCache(object):
foo.foo(5)
eq_(foo.calls, 3)

# Can't handle keyword arguments right now
with assert_raises(NotImplementedError):
foo.foo(3, asdf = 7)

# Verify that argspecs were maintained
eq_(inspect.getargspec(foo1),
inspect.ArgSpec(args=['n'],
varargs=None, keywords=None, defaults=None))
eq_(inspect.getargspec(foo.foo),
inspect.ArgSpec(args=['self', 'n'],
varargs=None, keywords=None, defaults=None))
varargs=None, keywords="kwargs", defaults=None))

Loading…
Cancel
Save