Replace far-too-clever decorator LRU cache with something simpler

This commit is contained in:
Aldo Cortesi 2015-03-22 21:00:41 +13:00
parent a2da38cc83
commit 842e23d3e3
5 changed files with 46 additions and 65 deletions

View File

@ -327,11 +327,7 @@ def ask_save_body(part, master, state, flow):
signals.status_message.send(message="No content to save.")
class FlowCache:
@utils.LRUCache(200)
def format_flow(self, *args):
return raw_format_flow(*args)
flowcache = FlowCache()
flowcache = utils.LRUCache(800)
def format_flow(f, focus, extended=False, hostheader=False, padding=2):
@ -370,6 +366,7 @@ def format_flow(f, focus, extended=False, hostheader=False, padding=2):
d["resp_ctype"] = t[0].split(";")[0]
else:
d["resp_ctype"] = ""
return flowcache.format_flow(
return flowcache.get(
raw_format_flow,
tuple(sorted(d.items())), focus, extended, padding
)

View File

@ -107,16 +107,7 @@ class FlowViewHeader(urwid.WidgetWrap):
)
class CallbackCache:
@utils.LRUCache(200)
def _callback(self, method, *args, **kwargs):
return getattr(self.obj, method)(*args, **kwargs)
def callback(self, obj, method, *args, **kwargs):
# obj varies!
self.obj = obj
return self._callback(method, *args, **kwargs)
cache = CallbackCache()
cache = utils.LRUCache(200)
class FlowView(urwid.WidgetWrap):
@ -158,8 +149,8 @@ class FlowView(urwid.WidgetWrap):
limit = sys.maxint
else:
limit = contentview.VIEW_CUTOFF
description, text_objects = cache.callback(
self, "_cached_content_view",
description, text_objects = cache.get(
self._cached_content_view,
viewmode,
tuple(tuple(i) for i in conn.headers.lst),
conn.content,

View File

@ -119,40 +119,33 @@ pkg_data = Data(__name__)
class LRUCache:
"""
A decorator that implements a self-expiring LRU cache for class
methods (not functions!).
Cache data is tracked as attributes on the object itself. There is
therefore a separate cache for each object instance.
A simple LRU cache for generated values.
"""
def __init__(self, size=100):
self.size = size
self.cache = {}
self.cacheList = []
def __call__(self, f):
cacheName = "_cached_%s"%f.__name__
cacheListName = "_cachelist_%s"%f.__name__
size = self.size
def get(self, gen, *args):
"""
gen: A (presumably expensive) generator function. The identity of
gen is NOT taken into account by the cache.
*args: A list of immutable arguments, used to establish identiy by
*the cache, and passed to gen to generate values.
"""
if self.cache.has_key(args):
self.cacheList.remove(args)
self.cacheList.insert(0, args)
return self.cache[args]
else:
ret = gen(*args)
self.cacheList.insert(0, args)
self.cache[args] = ret
if len(self.cacheList) > self.size:
d = self.cacheList.pop()
self.cache.pop(d)
return ret
@functools.wraps(f)
def wrap(self, *args):
if not hasattr(self, cacheName):
setattr(self, cacheName, {})
setattr(self, cacheListName, [])
cache = getattr(self, cacheName)
cacheList = getattr(self, cacheListName)
if cache.has_key(args):
cacheList.remove(args)
cacheList.insert(0, args)
return cache[args]
else:
ret = f(self, *args)
cacheList.insert(0, args)
cache[args] = ret
if len(cacheList) > size:
d = cacheList.pop()
cache.pop(d)
return ret
return wrap
def parse_content_type(c):
"""

View File

@ -62,38 +62,39 @@ def test_pretty_duration():
assert utils.pretty_duration(10) == "10.0s"
assert utils.pretty_duration(100) == "100s"
assert utils.pretty_duration(1000) == "1000s"
assert utils.pretty_duration(10000) == "10000s"
assert utils.pretty_duration(10000) == "10000s"
assert utils.pretty_duration(1.123) == "1.12s"
assert utils.pretty_duration(0.123) == "123ms"
def test_LRUCache():
cache = utils.LRUCache(2)
class Foo:
ran = False
@utils.LRUCache(2)
def one(self, x):
def gen(self, x):
self.ran = True
return x
f = Foo()
assert f.one(1) == 1
assert not f.ran
assert cache.get(f.gen, 1) == 1
assert f.ran
f.ran = False
assert f.one(1) == 1
assert cache.get(f.gen, 1) == 1
assert not f.ran
f.ran = False
assert f.one(1) == 1
assert cache.get(f.gen, 1) == 1
assert not f.ran
assert f.one(2) == 2
assert f.one(3) == 3
assert cache.get(f.gen, 2) == 2
assert cache.get(f.gen, 3) == 3
assert f.ran
f.ran = False
assert f.one(1) == 1
assert cache.get(f.gen, 1) == 1
assert f.ran
assert len(f._cached_one) == 2
assert len(f._cachelist_one) == 2
assert len(cache.cacheList) == 2
assert len(cache.cache) == 2
def test_unparse_url():
@ -128,4 +129,3 @@ def test_safe_subn():
def test_urlencode():
assert utils.urlencode([('foo','bar')])

View File

@ -2,8 +2,8 @@
# Generate a test pattern with pathoc
PATHOD=http://localhost:9999
pathoc localhost:8080 "get:'$PATHOD/p/200:p0,1:b@200b':b@200b"
pathoc localhost:8080 "get:'$PATHOD/p/300:p0,1:b@200b':b@200b"
pathoc localhost:8080 "get:'$PATHOD/p/400:p0,1:b@200b':b@200b"
pathoc localhost:8080 "get:'$PATHOD/p/500:p0,1:b@200b':b@200b"
pathoc localhost:8080 "get:'$PATHOD/p/600:p0,1:b@200b':b@200b"
pathoc localhost:8080 "get:'$PATHOD/p/200:p0,1:b@2048b':b@2048b"
pathoc localhost:8080 "get:'$PATHOD/p/300:p0,1:b@2048b':b@2048b"
pathoc localhost:8080 "get:'$PATHOD/p/400:p0,1:b@2048b':b@2048b"
pathoc localhost:8080 "get:'$PATHOD/p/500:p0,1:b@2048b':b@2048b"
pathoc localhost:8080 "get:'$PATHOD/p/600:p0,1:b@2048b':b@2048b"