Extra Methods in Fleche
When you decorate a function with @fleche, it’s not just a cached version of the original function. It also gains several helper methods that allow you to interact with the cache and the function’s metadata directly.
[1]:
from fleche import fleche
import time
No config file found. Using default memory cache.
[2]:
@fleche
def slow_add(a, b):
"""Adds two numbers slowly."""
time.sleep(1)
return a + b
1. .call(*args, **kwargs)
The .call method returns a Call object. This object captures all the information about a potential call to the function, including the function name, arguments, and metadata, without actually executing the function.
[3]:
call_info = slow_add.call(10, b=20)
print(f"Function Name: {call_info.name}")
print(f"Arguments: {call_info.arguments}")
print(f"Call Object: {call_info}")
Function Name: slow_add
Arguments: {'a': 10, 'b': 20}
Call Object: Call(name='slow_add', arguments={'a': 10, 'b': 20}, metadata={}, module='__main__', version=None, code_digest=None, result=None)
2. .digest(*args, **kwargs)
The .digest method returns the unique cache key (a SHA-256 hash) that fleche uses to identify this specific call in the cache. This is derived from the function name, arguments, and other metadata.
[4]:
key = slow_add.digest(10, b=20)
print(f"Cache Key: {key}")
Cache Key: 44a75ca3f502c3f7d86c9cb057840f8e1d07494e69f16f3e396bb4ae2a9d9a3e
3. .contains(*args, **kwargs)
You can use .contains to check if a result for a specific set of arguments is already present in the cache, without triggering the function execution.
[5]:
print(f"Is (10, 20) in cache? {slow_add.contains(10, b=20)}")
# Now we run it to populate the cache
slow_add(10, b=20)
print(f"Is (10, 20) in cache now? {slow_add.contains(10, b=20)}")
Is (10, 20) in cache? False
Is (10, 20) in cache now? True
4. .load(*args, **kwargs)
The .load method allows you to explicitly retrieve a result from the cache. If the result is not in the cache, it will raise a KeyError instead of executing the function.
[6]:
result = slow_add.load(10, b=20)
print(f"Loaded result: {result}")
try:
slow_add.load(1, 2)
except KeyError:
print("Result for (1, 2) not in cache.")
Loaded result: 30
Result for (1, 2) not in cache.
5. .rerun(*args, **kwargs)
The .rerun method allows you to force a re-execution of the function, even if the result is already in the cache. It will save the new result to the cache, and recursively force any nested @fleche calls to re-execute as well.
[7]:
start = time.time()
res = slow_add.rerun(10, b=20)
end = time.time()
print(f"Rerun Result: {res} (took {end - start:.2f} seconds forcing execution)")
# subsequent calls will be fast again
start = time.time()
res2 = slow_add(10, b=20)
end = time.time()
print(f"Normal call Result: {res2} (took {end - start:.2f} seconds)")
Rerun Result: 30 (took 1.00 seconds forcing execution)
Normal call Result: 30 (took 0.00 seconds)
6. .__wrapped__
Because fleche uses functools.wraps, the original undecorated function is always available via the .__wrapped__ attribute. This is useful if you want to bypass the cache entirely. Unlike .rerun, calling the .__wrapped__ function does not save the result to the cache, nor does it force nested cached functions to re-execute.
[8]:
start = time.time()
res = slow_add.__wrapped__(10, 20)
end = time.time()
print(f"Result: {res} (took {end - start:.2f} seconds bypassing cache)")
Result: 30 (took 1.00 seconds bypassing cache)