Skip to content

Commit

Permalink
- add asdict=True argument #35
Browse files Browse the repository at this point in the history
  • Loading branch information
zzzeek committed Jun 15, 2013
1 parent 630abea commit 4d28426
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 3 deletions.
2 changes: 1 addition & 1 deletion docs/build/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Changelog

.. change::
:tags: feature
:tickets: 33
:tickets: 33, 35

Added new methods :meth:`.CacheRegion.get_or_create_multi`
and :meth:`.CacheRegion.cache_multi_on_arguments`, which
Expand Down
39 changes: 37 additions & 2 deletions dogpile/cache/region.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def generate_key(*arg, **kw):
@my_region.cache_on_arguments(namespace=('x', 'y'))
def my_function(a, b, **kw):
return my_data()
:param function_multi_key_generator: Optional.
Similar to ``function_key_generator`` parameter, but it's used in
:meth:`.CacheRegion.cache_multi_on_arguments`. Generated function
Expand Down Expand Up @@ -924,7 +925,8 @@ def set_(value, *arg, **kw):
return decorator

def cache_multi_on_arguments(self, namespace=None, expiration_time=None,
should_cache_fn=None):
should_cache_fn=None,
asdict=False):
"""A function decorator that will cache multiple return
values from the function using a sequence of keys derived from the
function itself and the arguments passed to it.
Expand Down Expand Up @@ -991,6 +993,18 @@ def generate_something(*keys):
callable.
:param should_cache_fn: passed to :meth:`.CacheRegion.get_or_create_multi`.
This function is given a value as returned by the creator, and only
if it returns True will that value be placed in the cache.
:param asdict: if ``True``, the decorated function should return
its result as a dictionary of keys->values, and the final result
of calling the decorated function will also be a dictionary.
If left at its default value of ``False``, the decorated function
should return its result as a list of values, and the final
result of calling the decorated function will also be a list.
When ``asdict==True`` if the dictionary returned by the decorated
function is missing keys, those keys will not be cached.
.. versionadded:: 0.5.0
Expand All @@ -1015,9 +1029,30 @@ def creator(*keys_to_create):

timeout = expiration_time() if expiration_time_is_callable \
else expiration_time
return self.get_or_create_multi(keys, creator, timeout,

if asdict:
def dict_create(*keys):
d_values = creator(*keys)
return [d_values.get(key_lookup[k], NO_VALUE) for k in keys]

def wrap_cache_fn(value):
if value is NO_VALUE:
return False
elif not should_cache_fn:
return True
else:
return should_cache_fn(value)

result = self.get_or_create_multi(keys, dict_create, timeout,
wrap_cache_fn)
result = dict((k, v) for k, v in zip(cache_keys, result)
if v is not NO_VALUE)
else:
result = self.get_or_create_multi(keys, creator, timeout,
should_cache_fn)

return result

def invalidate(*arg):
keys = key_generator(*arg)
self.delete_multi(keys)
Expand Down
69 changes: 69 additions & 0 deletions tests/cache/test_region.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,75 @@ def generate(*args):
generate.set({7: 18, 10: 15})
eq_(generate(2, 7, 10), ['2 5', 18, 15])

def test_multi_asdict(self):
reg = self._region()

counter = itertools.count(1)

@reg.cache_multi_on_arguments(asdict=True)
def generate(*args):
return dict(
[(arg, "%d %d" % (arg, next(counter))) for arg in args]
)

eq_(generate(2, 8, 10), {2: '2 2', 8: '8 3', 10: '10 1'})
eq_(generate(2, 9, 10), {2: '2 2', 9: '9 4', 10: '10 1'})

generate.invalidate(2)
eq_(generate(2, 7, 10), {2: '2 5', 7: '7 6', 10: '10 1'})

generate.set({7: 18, 10: 15})
eq_(generate(2, 7, 10), {2: '2 5', 7: 18, 10: 15})

def test_multi_asdict_keys_missing(self):
reg = self._region()

counter = itertools.count(1)

@reg.cache_multi_on_arguments(asdict=True)
def generate(*args):
return dict(
[(arg, "%d %d" % (arg, next(counter)))
for arg in args if arg != 10]
)

eq_(generate(2, 8, 10), {2: '2 1', 8: '8 2'})
eq_(generate(2, 9, 10), {2: '2 1', 9: '9 3'})

assert reg.get(10) is NO_VALUE

generate.invalidate(2)
eq_(generate(2, 7, 10), {2: '2 4', 7: '7 5'})

generate.set({7: 18, 10: 15})
eq_(generate(2, 7, 10), {2: '2 4', 7: 18, 10: 15})

def test_multi_asdict_keys_missing_existing_cache_fn(self):
reg = self._region()

counter = itertools.count(1)

@reg.cache_multi_on_arguments(asdict=True,
should_cache_fn=lambda v: not v.startswith('8 '))
def generate(*args):
return dict(
[(arg, "%d %d" % (arg, next(counter)))
for arg in args if arg != 10]
)

eq_(generate(2, 8, 10), {2: '2 1', 8: '8 2'})
eq_(generate(2, 8, 10), {2: '2 1', 8: '8 3'})
eq_(generate(2, 8, 10), {2: '2 1', 8: '8 4'})
eq_(generate(2, 9, 10), {2: '2 1', 9: '9 5'})

assert reg.get(10) is NO_VALUE

generate.invalidate(2)
eq_(generate(2, 7, 10), {2: '2 6', 7: '7 7'})

generate.set({7: 18, 10: 15})
eq_(generate(2, 7, 10), {2: '2 6', 7: 18, 10: 15})

def test_multi_namespace(self):
reg = self._region()

Expand Down

0 comments on commit 4d28426

Please sign in to comment.