Commits

Andriy Kornatskyy committed 1d13ad0

Introduced args_key_builder that allows build cache key function at runtime.

Comments (0)

Files changed (2)

src/wheezy/caching/patterns.py

 """ ``patterns`` module.
 """
 
+from inspect import getargspec
 from time import sleep
 from time import time
 
     return get_or_create_wrapper
 
 
+def args_key_builder(key_prefix):
+    """ Returns a key builder that allows build a make cache key
+        function at runtime.
+
+        >>> def list_items(self, locale='en', sort_order=1):
+        ...     pass
+
+        >>> make_key = args_key_builder('repo')(list_items)
+        >>> make_key('self')
+        "repo-list_items:'en':1"
+        >>> make_key('self', 'uk')
+        "repo-list_items:'uk':1"
+        >>> make_key('self', sort_order=0)
+        "repo-list_items:'en':0"
+
+        Here is an example of make key function::
+
+            def key_list_items(self, locale='en', sort_order=1):
+                return "repo-list_items:%r:%r" % (locale, sort_order)
+
+    """
+    def build(f):
+        argnames, varargs, kwargs, defaults = getargspec(f)
+        if defaults:
+            n = len(defaults)
+            args = argnames[:-n]
+            args.extend('%s=%r' % x for x in zip(argnames[-n:], defaults))
+        else:
+            args = argnames
+        if argnames and argnames[0] in ('self', 'cls', 'klass'):
+            argnames = argnames[1:]
+        key_format = '%s-%s%s' % (key_prefix, f.__name__,
+                                  ':%r' * len(argnames))
+        fname = 'key_' + f.__name__
+        code = 'def %s(%s): return "%s" %% (%s)' % (
+            fname, ', '.join(args), key_format, ', '.join(argnames))
+        return compile_source(code, 'keys_' + key_prefix)[fname]
+    return build
+
+
 class OnePass(object):
     """ A solution to `Thundering Head` problem.
 
         if self.acquired:
             self.cache.delete(self.key, self.namespace)
             self.acquired = False
+
+
+# region: internal details
+
+def compile_source(source, name):
+    compiled = compile(source, name, 'exec')
+    local_vars = {}
+    exec(compiled, {}, local_vars)
+    return local_vars

src/wheezy/caching/tests/test_patterns.py

             'key', 'create_factory', 'dependency_factory',
             'time', 'namespace', self.mock_cache,
             'timeout', 'key_prefix')
+
+
+class KeyBuilderTestCase(unittest.TestCase):
+
+    def setUp(self):
+        from wheezy.caching.patterns import args_key_builder
+        self.mk = args_key_builder('prefix')
+
+    def test_noargs(self):
+        def items():
+            pass
+        assert 'prefix-items' == self.mk(items)()
+
+    def test_args(self):
+        def items(x, y):
+            pass
+        assert "prefix-items:'None':None" == self.mk(items)('None', None)
+
+    def test_defaults(self):
+        def items(x, y=''):
+            pass
+        assert "prefix-items:1:''" == self.mk(items)(1)
+        assert "prefix-items:1:'s'" == self.mk(items)(1, y='s')
+        assert "prefix-items:1:2" == self.mk(items)(1, y=2)
+
+    def test_sepecial(self):
+        def items(cls, y):
+            pass
+        assert 'prefix-items:1' == self.mk(items)('cls', 1)
+        assert 'prefix-items:None' == self.mk(items)('cls', None)
+        assert "prefix-items:''" == self.mk(items)('cls', '')