Commits

Andriy Kornatskyy committed 59cdb64

Introduced decorator wraps for get or add/set/create cache patterns.

  • Participants
  • Parent commits 1d13ad0

Comments (0)

Files changed (2)

src/wheezy/caching/patterns.py

     return get_or_add_wrapper
 
 
+def wraps_get_or_add(cache, key_builder, time=0, namespace=None):
+    """ Returns specialized decorator for `get_or_add` cache
+        pattern.
+
+        Example::
+
+            kb = key_builder('repo')
+            cached = wraps_get_or_add(cache, kb, time=100)
+
+            @cached
+            def list_items(self, locale):
+                pass
+    """
+    def decorate(wrapped):
+        make_key = key_builder(wrapped)
+
+        def get_or_add(*args, **kwargs):
+            key = make_key(*args, **kwargs)
+            result = cache.get(key, namespace)
+            if result is not None:
+                return result
+            result = wrapped(*args, **kwargs)
+            if result is not None:
+                cache.add(key, result, time, namespace)
+            return result
+        return get_or_add
+    return decorate
+
+
 def get_or_set(key, create_factory, dependency_factory=None,
                time=0, namespace=None, cache=None):
     """ Cache Pattern: get an item by *key* from *cache* and
     return get_or_set_wrapper
 
 
+def wraps_get_or_set(cache, key_builder, time=0, namespace=None):
+    """ Returns specialized decorator for `get_or_set` cache
+        pattern.
+
+        Example::
+
+            kb = key_builder('repo')
+            cached = wraps_get_or_set(cache, kb, time=100)
+
+            @cached
+            def list_items(self, locale):
+                pass
+    """
+    def decorate(wrapped):
+        make_key = key_builder(wrapped)
+
+        def get_or_set(*args, **kwargs):
+            key = make_key(*args, **kwargs)
+            result = cache.get(key, namespace)
+            if result is not None:
+                return result
+            result = wrapped(*args, **kwargs)
+            if result is not None:
+                cache.set(key, result, time, namespace)
+            return result
+        return get_or_set
+    return decorate
+
+
 def one_pass_create(key, create_factory, dependency_factory=None,
                     time=0, namespace=None, cache=None,
                     timeout=10, key_prefix='one_pass:'):
     return get_or_create_wrapper
 
 
-def args_key_builder(key_prefix):
+def wraps_get_or_create(cache, key_builder, time=0, namespace=None,
+                        timeout=10, key_prefix='one_pass:'):
+    """ Returns specialized decorator for `get_or_create` cache
+        pattern.
+
+        Example::
+
+            kb = key_builder('repo')
+            cached = wraps_get_or_create(cache, kb, time=100)
+
+            @cached
+            def list_items(self, locale):
+                pass
+    """
+    def decorate(wrapped):
+        make_key = key_builder(wrapped)
+
+        def get_or_create(*args, **kwargs):
+            key = make_key(*args, **kwargs)
+            result = cache.get(key, namespace)
+            if result is not None:
+                return result
+            return one_pass_create(
+                key, lambda: wrapped(*args, **kwargs), None,
+                time, namespace, cache,
+                timeout, key_prefix)
+        return get_or_create
+    return decorate
+
+
+def key_format(func, key_prefix):
+    """ Returns a key format for *func* and *key_prefix*.
+
+        >>> def list_items(self, locale='en', sort_order=1):
+        ...     pass
+        >>> key_format(list_items, 'repo')
+        'repo-list_items:%r:%r'
+    """
+    argnames = getargspec(func)[0]
+    n = len(argnames)
+    if n and argnames[0] in ('self', 'cls', 'klass'):
+        n -= 1
+    return '%s-%s%s' % (key_prefix, func.__name__, ':%r' * n)
+
+
+def key_formater(key_prefix):
+    """ Specialize a key format with *key_prefix*.
+
+        >>> def list_items(self, locale='en', sort_order=1):
+        ...     pass
+        >>> repo_key_format = key_formater('repo')
+        >>> repo_key_format(list_items)
+        'repo-list_items:%r:%r'
+    """
+    def key_format_wrapper(func):
+        return key_format(func, key_prefix)
+    return key_format_wrapper
+
+
+def 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)
+        >>> repo_key_builder = key_builder('repo')
+        >>> make_key = repo_key_builder(list_items)
         >>> make_key('self')
         "repo-list_items:'en':1"
         >>> make_key('self', 'uk')
                 return "repo-list_items:%r:%r" % (locale, sort_order)
 
     """
-    def build(f):
-        argnames, varargs, kwargs, defaults = getargspec(f)
+    def build(func):
+        argnames, varargs, kwargs, defaults = getargspec(func)
         if defaults:
             n = len(defaults)
             args = argnames[:-n]
             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__
+        fname = 'key_' + func.__name__
         code = 'def %s(%s): return "%s" %% (%s)' % (
-            fname, ', '.join(args), key_format, ', '.join(argnames))
+            fname, ', '.join(args), key_format(func, key_prefix),
+            ', '.join(argnames))
         return compile_source(code, 'keys_' + key_prefix)[fname]
     return build
 

src/wheezy/caching/tests/test_patterns.py

         mock_dependency_factory.return_value.add.assert_called_once_with(
             'key', 'ns')
 
+
+class PartialGetOrAddTestCase(GetOrAddTestCase):
+
     @patch('wheezy.caching.patterns.get_or_add')
     def test_partial(self, mock_get_or_add):
         """ Ensure call defaults.
             'time', 'namespace', 'cache')
 
 
+class WrapsGetOrAddTestCase(GetOrAddTestCase):
+
+    def setUp(self):
+        self.mock_cache = Mock()
+        self.mock_create_factory = Mock()
+
+    def get_or_add(self, dependency_factory=None):
+        from wheezy.caching.patterns import wraps_get_or_add as wga
+        key_builder = lambda f: lambda *args, **kwargs: 'key'
+        return wga(self.mock_cache, key_builder, 10, 'ns')(
+            self.mock_create_factory)()
+
+    def test_has_dependency(self):
+        """ Not supported.
+        """
+        pass
+
+
 class GetOrSetTestCase(unittest.TestCase):
 
     def setUp(self):
         self.mock_create_factory.assert_called_once_with()
         assert not self.mock_cache.add.called
 
+    def test_no_dependency(self):
+        """ There is specified `dependency_factory`.
+        """
+        self.mock_cache.get.return_value = None
+        self.mock_create_factory.return_value = 'x'
+        self.mock_cache.set.return_value = True
+
+        assert 'x' == self.get_or_set()
+        self.mock_cache.get.assert_called_once_with('key', 'ns')
+        self.mock_cache.set.assert_called_once_with('key', 'x', 10, 'ns')
+
     def test_has_dependency(self):
         """ There is specified `dependency_factory`.
         """
         mock_dependency_factory.return_value.add.assert_called_once_with(
             'key', 'ns')
 
+
+class PartialGetOrSetTestCase(GetOrSetTestCase):
+
     @patch('wheezy.caching.patterns.get_or_set')
     def test_partial(self, mock_get_or_set):
         """ Ensure call defaults.
             'time', 'namespace', 'cache')
 
 
+class WrapsGetOrSetTestCase(GetOrSetTestCase):
+
+    def setUp(self):
+        self.mock_cache = Mock()
+        self.mock_create_factory = Mock()
+
+    def get_or_set(self, dependency_factory=None):
+        from wheezy.caching.patterns import wraps_get_or_set as wgs
+        key_builder = lambda f: lambda *args, **kwargs: 'key'
+        return wgs(self.mock_cache, key_builder, 10, 'ns')(
+            self.mock_create_factory)()
+
+    def test_has_dependency(self):
+        """ Not supported.
+        """
+        pass
+
+
 class OnePassCreateTestCase(unittest.TestCase):
 
     def setUp(self):
 
         assert 'x' == self.one_pass_create()
 
+
+class PartialOnePassCreateTestCase(unittest.TestCase):
+
     @patch('wheezy.caching.patterns.one_pass_create')
     def test_partial(self, mock_one_pass_create):
         """ Ensure call defaults.
 
         assert 'x' == cached('key', None)
 
+
+class PartialGetOrCreateTestCase(GetOrCreateTestCase):
+
     @patch('wheezy.caching.patterns.one_pass_create')
     def test_partial(self, mock_one_pass_create):
         """ Ensure call defaults.
             'timeout', 'key_prefix')
 
 
+class WrapsGetOrCreateTestCase(GetOrCreateTestCase):
+
+    def setUp(self):
+        self.mock_cache = Mock()
+        self.mock_create_factory = Mock()
+
+    def get_or_create(self, dependency_factory=None):
+        from wheezy.caching.patterns import wraps_get_or_create as wgc
+        key_builder = lambda f: lambda *args, **kwargs: 'key'
+        return wgc(self.mock_cache, key_builder, 10, 'ns', 5)(
+            self.mock_create_factory)()
+
+
 class KeyBuilderTestCase(unittest.TestCase):
 
     def setUp(self):
-        from wheezy.caching.patterns import args_key_builder
-        self.mk = args_key_builder('prefix')
+        from wheezy.caching.patterns import key_builder
+        self.mk = key_builder('prefix')
 
     def test_noargs(self):
         def items():