Store the output from the decorated function in the cache and pull it
    from the cache on future invocations without rerunning.

    Normally, the value will be stored under a key which takes into account
    all of the parameters that are passed into it, thereby caching different
    invocations separately. If you specify a key, all invocations will be
    cached under that key, and different invocations will return the same
    value, which may be unexpected. So, be careful!

    If the cache is disabled, the decorated function will just run normally.

    Unlike the other functions in this module, you must pass a custom cache_obj
    to this() in order to operate on the non-global cache. This is because of
    wonky behavior when using decorator.decorator from a class method.

    :param func: (expensive?) function to decorate
    :param cache_obj: cache to a specific object (for use from the cache object itself)
    :param key: optional key to store the value under
    :param ttl: optional expiry to apply to the cached value
    :param *args: arg tuple to pass to the decorated function
    :param **kwargs: kwarg dict to pass to the decorated function

def this(func, cache_obj=CACHE_OBJ, key=None, ttl=None, *args, **kwargs):
    """
    Store the output from the decorated function in the cache and pull it
    from the cache on future invocations without rerunning.

    Normally, the value will be stored under a key which takes into account
    all of the parameters that are passed into it, thereby caching different
    invocations separately. If you specify a key, all invocations will be
    cached under that key, and different invocations will return the same
    value, which may be unexpected. So, be careful!

    If the cache is disabled, the decorated function will just run normally.

    Unlike the other functions in this module, you must pass a custom cache_obj
    to this() in order to operate on the non-global cache. This is because of
    wonky behavior when using decorator.decorator from a class method.

    :param func: (expensive?) function to decorate
    :param cache_obj: cache to a specific object (for use from the cache object itself)
    :param key: optional key to store the value under
    :param ttl: optional expiry to apply to the cached value
    :param *args: arg tuple to pass to the decorated function
    :param **kwargs: kwarg dict to pass to the decorated function
    """
    key = key or (func.__name__ + str(args) + str(kwargs))
    if cache_obj.has(key):
        return cache_obj.get(key)
    value = func(*args, **kwargs)
    cache_obj.upsert(key, value, ttl)
    return value