Flask-Cache Dynamic Timeout

If you have used Flask and are in need of caching views, then you will be familiar with Flask-cache. Flask-cache provides you with a very simple interface in order to setup cache for your application.

Flask-cache extension provides you with a way to set a specific time-out for your view function when you enable cache for that view as follows.

@cache.cached(timeout=50)
def index():
  return render_template('index.html')

But it does not provide you the ability to set time-outs dynamically. I came across a use case where I was calling a view function with different parameters and wanted the view to be cached until a fixed specific time and in my case it was midnight.

Below is the implementation of the cached decorator in Flask-cache

def cached(self, timeout=None, key_prefix='view/%s', unless=None):
    def decorator(f):
        @functools.wraps(f)
        def decorated_function(*args, **kwargs):
            ....
            if rv is None:
                rv = f(*args, **kwargs)
                try:
                self.cache.set(cache_key, rv,
                    timeout=decorated_function.cache_timeout)
            ....

        decorated_function.uncached = f
        decorated_function.cache_timeout = timeout
        decorated_function.make_cache_key = make_cache_key

        return decorated_function
    return decorator

You can see that the decorator directly passes the timeout parameter as a parameter to the cache.set function. So this removes the possibility of passing a function to dynamically set the timeout.

So I decided to write a decorator which decorates the cached decorator. Below is the decorator function I wrote that sets the time-out to 23:59 hours.

def cache_timeout(f):
    @functools.wraps(f)
    def decorated_function(*args, **kwargs):
        now = datetime.datetime.now()
        deadline = now.replace(hour=23, minute=59)
        period = (deadline - now)
        f.cache_timeout = period.seconds
        return f(*args, **kwargs)
    return decorated_function

The cache_timeout decorator can then be used as

@app.route('/stats', methods=['GET'])
@cache_timeout
@cache.cached()
def stats():
    return render_template('index.html')

Please note that location of @cache_timeout and @cache.cached() cannot be interchanged.

Currently the decorator does not accept any parameters, but it can be easily extended to include parameters and perform cache time-out operations to your satisfaction.