Permalink
Browse files

huge ugly commit.

  • Loading branch information...
1 parent e325553 commit 511a1eaa20de25e4dd8891850b4aa8eac7017141 @jneen committed Apr 5, 2012
Showing with 161 additions and 74 deletions.
  1. +21 −0 LICENSE
  2. +0 −71 cache/__init__.py
  3. +0 −1 cache/version.py
  4. +15 −2 setup.py
  5. +73 −0 src/cache/__init__.py
  6. +1 −0 src/cache/version.py
  7. +51 −0 test/cache_test.py
View
21 LICENSE
@@ -0,0 +1,21 @@
+# MIT license. See http://www.opensource.org/licenses/mit-license.php
+
+Copyright (c) 2012 Desmos, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
View
@@ -1,71 +0,0 @@
-from decorator import decorator
-from time import sleep
-from inspect import getargspec
-
-from .version import VERSION
-
-class TestCache:
- def __init__(self):
- self._cache = {}
-
- def set(self, key, val, **kw):
- self._cache[key] = val
-
- def get(self, key):
- return self._cache.get(key)
-
-class Cache:
- def __init__(self, cache):
- self.cache = cache
-
- def __call__(self, key, **kw):
- def _cache(fn):
- return CacheWrapper(self.cache, key, fn, **kw)
-
- return _cache
-
-class CacheWrapper:
- def __init__(self, cache, key, calculate, **kw):
- self.cache = cache
- self.key = key
- self.calculate = calculate
- self.options = kw
-
- if len(getargspec(calculate).args) > 0:
- raise TypeError("cannot cache a function with arguments")
-
- def cached(self, default='__absent__'):
- cached = self.cache.get(self.key)
- if cached is None:
- if default == '__absent__':
- raise KeyError
-
- return default
-
- return cached
-
- def refresh(self, *args, **kwargs):
- fresh = self.calculate(*args, **kwargs)
- self.cache.set(self.key, fresh, **self.options)
- return fresh
-
- def conditionally_calculate(self, *args, **kwargs):
- try:
- return self.cached()
- except KeyError:
- return self.refresh(*args, **kwargs)
-
- def __call__(self, *args, **kwargs):
- return self.conditionally_calculate(*args, **kwargs)
-
-
-if __name__ == '__main__':
- cache = Cache(TestCache())
-
- @cache("my_cache/expensive", ttl=100)
- def expensive():
- print "===== calculating ====="
- return "done."
-
- expensive.cached(default=3)
- expensive.refresh()
View
@@ -1 +0,0 @@
-VERSION = "0.0.1"
View
@@ -1,8 +1,21 @@
from setuptools import setup, find_packages
-from cache.version import VERSION
+from cache.version import __version__
setup(
name="cache",
- version=VERSION,
+ version=__version__,
+ description="caching for humans",
+ author="Jay Adkisson",
+ author_email="j4yferd at gmail dot com (humans only, please)",
+ license="MIT",
+ classifiers=[
+ "Development Status :: 3 - Alpha",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+ "Topic :: Database :: Front-Ends",
+ "License :: OSI Approved :: MIT License",
+ ],
+ requires=["decorator"],
+ keywords="cache decorator humans",
packages=find_packages(),
)
View
@@ -0,0 +1,73 @@
+# local dependencies
+from .version import __version__
+
+# external dependencies
+from decorator import decorator
+
+# stdlib
+from inspect import getargspec
+
+class Cache:
+ DEFAULT_OPTIONS = {
+ 'enabled': True
+ }
+
+ def __init__(self, cache, **default_options):
+ self.cache = cache
+ self.default_options = self.DEFAULT_OPTIONS.copy()
+ self.default_options.update(default_options)
+
+ def __call__(self, key, **kw):
+ opts = self.default_options.copy()
+ opts.update(kw)
+
+ def _cache(fn):
+ return CacheWrapper(self.cache, key, fn, **opts)
+
+ return _cache
+
+class CacheWrapper:
+ def __init__(self, cache, key, calculate, **kw):
+ self.cache = cache
+ self.key = key
+ self.calculate = calculate
+ self.options = kw
+
+ if len(getargspec(calculate).args) > 0:
+ raise TypeError("cannot cache a function with arguments")
+
+ def cached(self, default='__absent__'):
+ cached = None
+ if self.options.get('enabled') and not self.options.get('bust'):
+ cached = self.cache.get(self.key)
+
+ if cached is None:
+ if default == '__absent__':
+ raise KeyError
+
+ return default
+
+ return self._unprepare_value(cached)
+
+ def _prepare_value(self, value):
+ return { 'value': value }
+
+ def _unprepare_value(self, prepared):
+ return prepared['value']
+
+ def refresh(self):
+ fresh = self.calculate()
+ if self.options.get('enabled'):
+ value = self._prepare_value(fresh)
+ self.cache.set(self.key, value, **self.options)
+
+ return fresh
+
+ def get(self):
+ try:
+ return self.cached()
+ except KeyError:
+ return self.refresh()
+
+ def __call__(self):
+ return self.get()
View
@@ -0,0 +1 @@
+__version__ = "0.0.1"
View
@@ -0,0 +1,51 @@
+from cache import Cache
+
+class TestCache:
+ def __init__(self):
+ self._cache = {}
+
+ def set(self, key, val, **kw):
+ self._cache[key] = val
+
+ def get(self, key):
+ return self._cache.get(key)
+
+def call_counter():
+ state = { 'count' : 0 }
+ def _call_counter():
+ state['count'] += 1
+ return state['count']
+
+ return _call_counter
+
+
+# begin tests
+def test_basic():
+ backend = TestCache()
+ cache = Cache(backend, enabled=True)
+
+ c = cache("counter")(call_counter())
+
+ assert c() == 1 # called the first time
+ assert c() == 1 # not called the second time
+ assert backend.get("counter")
+
+def test_disable():
+ backend = TestCache()
+ cache = Cache(backend, enabled=False)
+
+ c = cache("counter")(call_counter())
+
+ assert c() == 1 # called the first time
+ assert c() == 2 # called the second time too
+ assert not backend.get("counter")
+
+def test_bust():
+ backend = TestCache()
+ cache = Cache(backend, bust=True)
+
+ c = cache("counter")(call_counter())
+
+ assert c() == 1
+ assert c() == 2
+ assert backend.get("counter")

0 comments on commit 511a1ea

Please sign in to comment.