Package: hscacheutils
[![Build Status](https://travis-ci.org/HubSpot/hscacheutils.png?branch=master)](https://travis-ci.org/HubSpot/hscacheutils)A python implementation of [generational caching](http://www.regexprn.com/2011/06/web-application-caching-strategies_05.html).Three main interfaces are provided:- Direct methods `gen_cache.get`, `gen_cache.set`, `gen_cache.invalidate`, and `gen_cache.delete`- A `CustomUseGenCache` instance- A function decorator `@gen_cache.wrap`## Direct methods:```pythonfrom hscacheutils.generational_cache import gen_cachehtml = gen_cache.get(('nav', 'nav_portal:user_id'), user_id=1)gen_cache.set('<html>', ('nav', 'nav_portal:user_id'), user_id=1)gen_cache.invalidate(('nav', 'nav_portal:user_id'), user_id=1)```## A `CustomUseGenCache` instanceThe same as the direct methods, but creates an object so that you con't have to keep on passing in the generation names every single time.```pythoncustom_cache = CustomUseGenCache(['customgenz:user_id','customgenz:blog_id'])blog_id = 17user_id = 123key = random.randint(1, 20000000)val = custom_cache.get(blog_id=blog_id, user_id=123, cache_key=key)custom_cache.set(value=first_val, blog_id=blog_id, user_id=123, cache_key=key)custom_cache.invalidate(user_id=123)custom_cache.delete(blog_id=blog_id, user_id=123, cache_key=key)```## The `@gen_cache.wrap` decoratorIt can be applied to function, method or classmethod. It is mostly similar to gen_cache.get, but with some additional magic to make your life easier.### Magic #1: (true for all 3 interfaces)The contents of "value-based" generations are automatically pulled from the arguments in wrapped function. Eg.```python@gen_cache.wrap('project_name', 'foo_per_user_id:user_id')def foobar(user_id):...foobar(53)   # Uses 'project_name' and 'foo_per_user_id:53' as generationsfoobar(999)  # Uses 'project_name' and 'foo_per_user_id:999' as generations# So when invalidating like so...gen_cache.invalidate("for_per_user_id:user_id", user_id=999)foobar(53)   # This is still cachedfoobar(999)  # This has been invalidated```So the when foobar is called the ':user_id' part of the value-based generation looks for anyargument named "user_id", then takes its value to create a generation such as "for_per_user_id:53".This means that the "for_per_user_id" generation is only invalidated on a per-portal basis### Magic #2: (true for all 3 interfaces)All of the arguments (not used in value-based generations described above) are automatically appended to the cache key. Eg.```python@gen_cache.wrap('whatever')def foobar(something, another=False):...# XXX represents the current counter value of the 'whatever' generationfoobar(1)                # Uses a cache key roughly like: "whatever:XXX [1]{another=False}"foobar(2)                # Uses a cache key roughly like: "whatever:XXX [2]{another=False}"foobar(2, another=True)  # Uses a cache key roughly like: "whatever:XXX [2]{another=True}"# So when invalidating like so...gen_cache.invalidate("whatever")foobar(1)                # This has been invalidatedfoobar(2)                # This has been invalidatedfoobar(2, another=True)  # This has been invalidated```If you don't what this behavior for one or more arguments, make sure to put the name of thatargument(s) in the "exclude" option (see below).### Magic #3: (only true for the decorator)The cache key will automatically include the current module name, function name, andline number. So when this function moves to a different file, is renamed, or moves up or down afew lines, the cache will automatically be invalidated.(Note, I'm not sure this file/function name magic is worth keeping)### EXTRASThe wrapped callable gets `invalidate` methods. Call `invalidate` withsame arguments as function and the result for these arguments will beinvalidated.### KEYWORD OPTIONStimeout=3600 (defaults to None) is the number of seconds before this cache should expireexclude=[...] (defaults to empty list) is all the arguments you do not want to automaticaaly bea included in the cache key.log_misses=True (False by default) will print out some debugging into on every cache missignore_locally=True (False by default) will disable this caching when ENV == 'local'### REAL `gen_cache.wraps` cache key example[cached]hsdjango.test.test_generational_cache.func_with_lots_of_args:369(['one','two']{'project':1336056824437339,'foobar':'NOThello','user_id':42})^                        ^                          ^             ^        ^                   ^                                ^|                        |                          |             |        |                   |                                |prefix                 module name                 func name       line #     |   generation & current counter value               ||                                                    |non-excluded positional args                         non-excluded keyword argsGotcha #1: Be careful to use either "self" or "cls" as the first argument name when wrappingmethods and classmethods. This code relies on those names (see _func_type) to automaticallychop off the first argument from the cache key._Note: based on (and built re-using) django-cache-utils._