-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adding a basic memory cache, scoped to a block
- Loading branch information
Showing
3 changed files
with
132 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
from unittest import TestCase | ||
from threading import currentThread | ||
from restclients_core.util.local_cache import (local_cache, set_cache_value, | ||
get_cache_value, LOCAL_CACHE) | ||
|
||
|
||
class TestCache(TestCase): | ||
def test_context(self): | ||
thread = currentThread() | ||
with local_cache() as lc: | ||
v1 = test_method1("init") | ||
|
||
self.assertTrue(thread in LOCAL_CACHE) | ||
v2 = test_method1("nope, old cached value") | ||
self.assertEquals(v2, "init") | ||
|
||
self.assertFalse(thread in LOCAL_CACHE) | ||
|
||
def test_decorator(self): | ||
thread = currentThread() | ||
|
||
@local_cache() | ||
def use_the_cache(): | ||
v1 = test_method1("init decorator") | ||
|
||
self.assertTrue(thread in LOCAL_CACHE) | ||
v2 = test_method1("nope, old cached value") | ||
self.assertEquals(v2, "init decorator") | ||
|
||
use_the_cache() | ||
self.assertFalse(thread in LOCAL_CACHE) | ||
|
||
def test_no_cache(self): | ||
v1 = test_method1("init none") | ||
self.assertEquals(v1, "init none") | ||
|
||
v2 = test_method1("second, none") | ||
self.assertEquals(v2, "second, none") | ||
|
||
|
||
def test_method1(val): | ||
key = "test_method1_key" | ||
value = get_cache_value(key) | ||
if value: | ||
return value | ||
|
||
set_cache_value(key, val) | ||
return val |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
from threading import currentThread | ||
|
||
|
||
LOCAL_CACHE = {} | ||
|
||
|
||
class local_cache(object): | ||
""" | ||
A decorator or context manager that will cache some values for the | ||
lifetime of the decorator/context manager. | ||
""" | ||
def _function(self, func, *args, **kwargs): | ||
def wrapped(*args, **kwargs): | ||
try: | ||
self.__enter__() | ||
value = func(*args, **kwargs) | ||
finally: | ||
self.__exit__() | ||
return value | ||
return wrapped | ||
|
||
# This handles function decorators + class decorators. Needs to switch | ||
# on argument type :( | ||
def __call__(self, decorated, *args, **kwargs): | ||
if isinstance(decorated, type): | ||
raise Exception("Can only decorate a function, not a class") | ||
|
||
return self._function(decorated, *args, **kwargs) | ||
|
||
# These handle context managers, and are used by the others | ||
def __enter__(self, *args, **kwargs): | ||
thread = currentThread() | ||
|
||
LOCAL_CACHE[thread] = {} | ||
return self | ||
|
||
def __exit__(*args, **kwargs): | ||
thread = currentThread() | ||
del LOCAL_CACHE[thread] | ||
|
||
|
||
def _get_local_cache(): | ||
thread = currentThread() | ||
if thread in LOCAL_CACHE: | ||
return LOCAL_CACHE[thread] | ||
if hasattr(thread, 'parent'): | ||
thread = thread.parent | ||
if thread in LOCAL_CACHE: | ||
return LOCAL_CACHE[thread] | ||
|
||
return | ||
|
||
|
||
def set_cache_value(key, value): | ||
""" | ||
If local cache is being used, this will set a cache value for the | ||
lifetime of that cache. | ||
""" | ||
cache = _get_local_cache() | ||
if cache is None: | ||
return | ||
|
||
cache[key] = value | ||
|
||
|
||
def get_cache_value(key): | ||
""" | ||
If local cache is being used, this will get the cache value, if it exists. | ||
""" | ||
cache = _get_local_cache() | ||
if cache is None: | ||
return | ||
|
||
if key in cache: | ||
return cache[key] |