From 842e23d3e386169d9a90cef2a634c55a3e5fdd8e Mon Sep 17 00:00:00 2001 From: Aldo Cortesi Date: Sun, 22 Mar 2015 21:00:41 +1300 Subject: [PATCH] Replace far-too-clever decorator LRU cache with something simpler --- libmproxy/console/common.py | 9 +++---- libmproxy/console/flowview.py | 15 +++-------- libmproxy/utils.py | 51 +++++++++++++++-------------------- test/test_utils.py | 26 +++++++++--------- test/tools/testpatt | 10 +++---- 5 files changed, 46 insertions(+), 65 deletions(-) diff --git a/libmproxy/console/common.py b/libmproxy/console/common.py index a0590bb1d..2f143f013 100644 --- a/libmproxy/console/common.py +++ b/libmproxy/console/common.py @@ -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 ) diff --git a/libmproxy/console/flowview.py b/libmproxy/console/flowview.py index e864cf471..2c847fbaf 100644 --- a/libmproxy/console/flowview.py +++ b/libmproxy/console/flowview.py @@ -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, diff --git a/libmproxy/utils.py b/libmproxy/utils.py index 51f2dc26a..5ed70a45e 100644 --- a/libmproxy/utils.py +++ b/libmproxy/utils.py @@ -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): """ diff --git a/test/test_utils.py b/test/test_utils.py index 78d1c0725..1678a7dee 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -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')]) - diff --git a/test/tools/testpatt b/test/tools/testpatt index d4546d483..5ee1ea025 100755 --- a/test/tools/testpatt +++ b/test/tools/testpatt @@ -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"