Commits

Mike Bayer committed 85ad945

:meth:`.CacheRegion.get_or_create` and
:meth:`.CacheRegion.cache_on_arguments` now accept a new
argument ``should_cache_fn``, receives the value
returned by the "creator" and then returns True or
False, where True means "cache plus return",
False means "return the value but don't cache it."

Comments (0)

Files changed (4)

docs/build/changelog.rst

 Changelog
 ==============
 .. changelog::
+    :version: 0.4.3
+
+    .. change::
+        :tags: feature
+        :pullreq: 13
+
+      :meth:`.CacheRegion.get_or_create` and
+      :meth:`.CacheRegion.cache_on_arguments` now accept a new
+      argument ``should_cache_fn``, receives the value
+      returned by the "creator" and then returns True or
+      False, where True means "cache plus return",
+      False means "return the value but don't cache it."
+
+.. changelog::
     :version: 0.4.2
     :released: Sat Jan 19 2013
 

dogpile/cache/__init__.py

-__version__ = '0.4.2'
+__version__ = '0.4.3'
 
 from .region import CacheRegion, register_backend, make_region

dogpile/cache/region.py

             if expiration_time is None:
                 expiration_time = self.expiration_time
             if expiration_time is not None and \
-                time.time() - value.metadata["ct"] > expiration_time:
+                  time.time() - value.metadata["ct"] > expiration_time:
                 return NO_VALUE
             elif self._invalidated and value.metadata["ct"] < self._invalidated:
                 return NO_VALUE
 
         return value.payload
 
-    def get_or_create(self, key, creator, expiration_time=None):
+    def get_or_create(self, key, creator, expiration_time=None,
+                                should_cache_fn=None):
         """Return a cached value based on the given key.
 
         If the value does not exist or is considered to be expired
          the expiration time already configured on this :class:`.CacheRegion`
          if not None.   To set no expiration, use the value -1.
 
+        :param should_cache_fn: optional callable function which will receive the
+         value returned by the "creator", and will then return True or False,
+         indicating if the value should actually be cached or not.  If it
+         returns False, the value is still returned, but isn't cached.
+         E.g.::
+
+            def dont_cache_none(value):
+                return value is not None
+
+            value = region.get_or_create("some key",
+                                create_value,
+                                should_cache_fn=dont_cache_none)
+
+         Above, the function returns the value of create_value() if
+         the cache is invalid, however if the return value is None,
+         it won't be cached.
+
+         .. versionadded:: 0.4.3
+
         See also:
 
         :meth:`.CacheRegion.cache_on_arguments` - applies :meth:`.get_or_create`
             return value.payload, value.metadata["ct"]
 
         def gen_value():
-            value = self._value(creator())
-            if value.payload is not NO_VALUE:
+            created_value = creator()
+            value = self._value(created_value)
+
+            if not should_cache_fn or \
+                    should_cache_fn(created_value):
                 self.backend.set(key, value)
+
             return value.payload, value.metadata["ct"]
 
         if expiration_time is None:
 
         self.backend.delete(key)
 
-    def cache_on_arguments(self, namespace=None, expiration_time=None):
+    def cache_on_arguments(self, namespace=None, expiration_time=None,
+                                        should_cache_fn=None):
         """A function decorator that will cache the return
         value of the function using a key derived from the
         function itself and its arguments.
          being declared.
         :param expiration_time: if not None, will override the normal
          expiration time.
+        :param should_cache_fn: passed to :meth:`.CacheRegion.get_or_create`.
+
+          .. versionadded:: 0.4.3
+
         """
         def decorator(fn):
             key_generator = self.function_key_generator(namespace, fn)
                 @wraps(fn)
                 def creator():
                     return fn(*arg, **kw)
-                return self.get_or_create(key, creator, expiration_time)
+                return self.get_or_create(key, creator, expiration_time,
+                                                should_cache_fn)
 
             def invalidate(*arg, **kw):
                 key = key_generator(*arg, **kw)

tests/cache/test_region.py

         return reg
 
     def test_instance_from_dict(self):
-        my_conf = { 
+        my_conf = {
             'cache.example.backend': 'mock',
             'cache.example.expiration_time': 600,
             'cache.example.arguments.url': '127.0.0.1'
-            } 
+            }
         my_region = make_region()
         my_region.configure_from_config(my_conf, 'cache.example.')
         eq_(my_region.expiration_time, 600)
         my_region.configure_from_config(dict(config.items('xyz')), 'cache.example.')
         eq_(my_region.expiration_time, 600)
         assert isinstance(my_region.backend, MockBackend) is True
-        eq_(my_region.backend.arguments, {'url': '127.0.0.1', 
+        eq_(my_region.backend.arguments, {'url': '127.0.0.1',
                             'dogpile_lockfile':False, 'xyz':None})
 
     def test_key_mangler_argument(self):
         counter = itertools.count(1)
         def creator():
             return "some value %d" % next(counter)
-        eq_(reg.get_or_create("some key", creator, expiration_time=1), 
+        eq_(reg.get_or_create("some key", creator, expiration_time=1),
                     "some value 1")
         time.sleep(2)
         eq_(reg.get("some key"), "some value 1")
-        eq_(reg.get_or_create("some key", creator, expiration_time=1), 
+        eq_(reg.get_or_create("some key", creator, expiration_time=1),
                     "some value 2")
         eq_(reg.get("some key"), "some value 2")
 
         counter = itertools.count(1)
         def creator():
             return "some value %d" % next(counter)
-        eq_(reg.get_or_create("some key", creator), 
+        eq_(reg.get_or_create("some key", creator),
                     "some value 1")
 
         reg.invalidate()
-        eq_(reg.get_or_create("some key", creator), 
+        eq_(reg.get_or_create("some key", creator),
                     "some value 2")
 
-    def test_not_cache_NO_VALUE(self):
+    def test_should_cache_fn(self):
         reg = self._region()
+        values = [1, 2, 3]
         def creator():
-            return NO_VALUE
-        reg.get_or_create("some key", creator)
-        self.assertNotIn("some key", reg.backend._cache)
+            return values.pop(0)
+        should_cache_fn = lambda val: val in (1, 3)
+        ret = reg.get_or_create(
+                    "some key", creator,
+                    should_cache_fn=should_cache_fn)
+        eq_(ret, 1)
+        eq_(reg.backend._cache['some key'][0], 1)
+        reg.invalidate()
+        ret = reg.get_or_create(
+                    "some key", creator,
+                    should_cache_fn=should_cache_fn)
+        eq_(ret, 2)
+        eq_(reg.backend._cache['some key'][0], 1)
+        reg.invalidate()
+        ret = reg.get_or_create(
+                    "some key", creator,
+                    should_cache_fn=should_cache_fn)
+        eq_(ret, 3)
+        eq_(reg.backend._cache['some key'][0], 3)
 
 class CacheDecoratorTest(TestCase):
     def _region(self, init_args={}, config_args={}, backend="mock"):