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.") signals.status_message.send(message="No content to save.")
class FlowCache: flowcache = utils.LRUCache(800)
@utils.LRUCache(200)
def format_flow(self, *args):
return raw_format_flow(*args)
flowcache = FlowCache()
def format_flow(f, focus, extended=False, hostheader=False, padding=2): 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] d["resp_ctype"] = t[0].split(";")[0]
else: else:
d["resp_ctype"] = "" d["resp_ctype"] = ""
return flowcache.format_flow( return flowcache.get(
raw_format_flow,
tuple(sorted(d.items())), focus, extended, padding tuple(sorted(d.items())), focus, extended, padding
) )

View File

@ -107,16 +107,7 @@ class FlowViewHeader(urwid.WidgetWrap):
) )
class CallbackCache: cache = utils.LRUCache(200)
@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()
class FlowView(urwid.WidgetWrap): class FlowView(urwid.WidgetWrap):
@ -158,8 +149,8 @@ class FlowView(urwid.WidgetWrap):
limit = sys.maxint limit = sys.maxint
else: else:
limit = contentview.VIEW_CUTOFF limit = contentview.VIEW_CUTOFF
description, text_objects = cache.callback( description, text_objects = cache.get(
self, "_cached_content_view", self._cached_content_view,
viewmode, viewmode,
tuple(tuple(i) for i in conn.headers.lst), tuple(tuple(i) for i in conn.headers.lst),
conn.content, conn.content,

View File

@ -119,40 +119,33 @@ pkg_data = Data(__name__)
class LRUCache: class LRUCache:
""" """
A decorator that implements a self-expiring LRU cache for class A simple LRU cache for generated values.
methods (not functions!).
Cache data is tracked as attributes on the object itself. There is
therefore a separate cache for each object instance.
""" """
def __init__(self, size=100): def __init__(self, size=100):
self.size = size self.size = size
self.cache = {}
self.cacheList = []
def __call__(self, f): def get(self, gen, *args):
cacheName = "_cached_%s"%f.__name__ """
cacheListName = "_cachelist_%s"%f.__name__ gen: A (presumably expensive) generator function. The identity of
size = self.size 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): def parse_content_type(c):
""" """

View File

@ -67,33 +67,34 @@ def test_pretty_duration():
assert utils.pretty_duration(0.123) == "123ms" assert utils.pretty_duration(0.123) == "123ms"
def test_LRUCache(): def test_LRUCache():
cache = utils.LRUCache(2)
class Foo: class Foo:
ran = False ran = False
@utils.LRUCache(2) def gen(self, x):
def one(self, x):
self.ran = True self.ran = True
return x return x
f = Foo() f = Foo()
assert f.one(1) == 1
assert not f.ran
assert cache.get(f.gen, 1) == 1
assert f.ran assert f.ran
f.ran = False f.ran = False
assert f.one(1) == 1 assert cache.get(f.gen, 1) == 1
assert not f.ran assert not f.ran
f.ran = False f.ran = False
assert f.one(1) == 1 assert cache.get(f.gen, 1) == 1
assert not f.ran assert not f.ran
assert f.one(2) == 2 assert cache.get(f.gen, 2) == 2
assert f.one(3) == 3 assert cache.get(f.gen, 3) == 3
assert f.ran assert f.ran
f.ran = False f.ran = False
assert f.one(1) == 1 assert cache.get(f.gen, 1) == 1
assert f.ran assert f.ran
assert len(f._cached_one) == 2 assert len(cache.cacheList) == 2
assert len(f._cachelist_one) == 2 assert len(cache.cache) == 2
def test_unparse_url(): def test_unparse_url():
@ -128,4 +129,3 @@ def test_safe_subn():
def test_urlencode(): def test_urlencode():
assert utils.urlencode([('foo','bar')]) assert utils.urlencode([('foo','bar')])

View File

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