2024-12-05 12:53:05 +11:00
|
|
|
import os
|
|
|
|
import json
|
|
|
|
import hashlib
|
2024-12-05 12:59:24 +11:00
|
|
|
import threading
|
2024-12-05 12:53:05 +11:00
|
|
|
from functools import wraps
|
2024-12-05 12:59:24 +11:00
|
|
|
from time import time, sleep
|
2024-12-05 12:53:05 +11:00
|
|
|
|
2024-12-09 15:43:22 +11:00
|
|
|
def file_cache(ttl=3600):
|
2024-12-05 12:53:05 +11:00
|
|
|
"""
|
|
|
|
Decorator to cache function results in the specified folder with a TTL.
|
|
|
|
|
|
|
|
Args:
|
|
|
|
folder (str): Directory where cached files will be stored.
|
|
|
|
ttl (int): Time-to-live for the cache in seconds.
|
|
|
|
"""
|
2024-12-09 15:43:22 +11:00
|
|
|
folder="cache"
|
2024-12-05 12:53:05 +11:00
|
|
|
if not os.path.exists(folder):
|
|
|
|
os.makedirs(folder)
|
|
|
|
|
2024-12-05 12:59:24 +11:00
|
|
|
def refresh_cache(func, cache_file, cache_key, args, kwargs):
|
|
|
|
def refresh_loop():
|
|
|
|
while True:
|
2024-12-11 13:31:52 +11:00
|
|
|
sleep(ttl)
|
2024-12-05 12:59:24 +11:00
|
|
|
try:
|
|
|
|
if os.path.exists(cache_file):
|
|
|
|
with open(cache_file, "r") as f:
|
|
|
|
cached_data = json.load(f)
|
2024-12-11 13:31:52 +11:00
|
|
|
if time() - cached_data["timestamp"] < ttl:
|
2024-12-05 12:59:24 +11:00
|
|
|
return
|
|
|
|
|
|
|
|
# Re-compute the result and update the cache
|
|
|
|
result = func(*args, **kwargs)
|
|
|
|
with open(cache_file, "w") as f:
|
|
|
|
json.dump({"timestamp": time(), "result": result}, f)
|
|
|
|
except Exception as e:
|
2024-12-12 13:49:20 +11:00
|
|
|
print(f"Error during cache refresh of {cache_file} (Name: {func.__name__}): {e}")
|
2024-12-05 12:59:24 +11:00
|
|
|
|
2024-12-05 14:32:26 +11:00
|
|
|
threading.Thread(target=refresh_loop).start()
|
2024-12-05 12:59:24 +11:00
|
|
|
|
|
|
|
|
2024-12-05 12:53:05 +11:00
|
|
|
def decorator(func):
|
|
|
|
@wraps(func)
|
|
|
|
def wrapper(*args, **kwargs):
|
|
|
|
# Create a unique cache key based on the function name and arguments
|
|
|
|
cache_key = hashlib.md5(
|
|
|
|
f"{func.__name__}-{args}-{kwargs}".encode("utf-8")
|
|
|
|
).hexdigest()
|
|
|
|
cache_file = os.path.join(folder, f"{cache_key}.json")
|
2024-12-05 12:59:24 +11:00
|
|
|
|
|
|
|
# Start a background thread for auto-refresh if it doesn't exist
|
|
|
|
if not os.path.exists(cache_file):
|
|
|
|
result = func(*args, **kwargs)
|
|
|
|
with open(cache_file, "w") as f:
|
|
|
|
json.dump({"timestamp": time(), "result": result}, f)
|
|
|
|
|
|
|
|
# Start refresh thread
|
|
|
|
refresh_cache(func, cache_file, cache_key, args, kwargs)
|
|
|
|
|
2024-12-05 12:53:05 +11:00
|
|
|
# Check if cache exists and is valid
|
|
|
|
if os.path.exists(cache_file):
|
|
|
|
try:
|
|
|
|
with open(cache_file, "r") as f:
|
|
|
|
cached_data = json.load(f)
|
|
|
|
# Check if the cache has expired
|
2024-12-11 13:31:52 +11:00
|
|
|
if time() - cached_data["timestamp"] < (ttl * 2):
|
2024-12-05 14:32:26 +11:00
|
|
|
refresh_cache(func, cache_file, cache_key, args, kwargs)
|
2024-12-05 12:53:05 +11:00
|
|
|
return cached_data["result"]
|
|
|
|
except (IOError, ValueError, KeyError):
|
|
|
|
pass # In case of error, re-compute the result
|
2024-12-05 12:59:24 +11:00
|
|
|
|
|
|
|
# If cache is expired or invalid, recompute and update
|
2024-12-05 12:53:05 +11:00
|
|
|
result = func(*args, **kwargs)
|
2024-12-05 12:59:24 +11:00
|
|
|
with open(cache_file, "w") as f:
|
|
|
|
json.dump({"timestamp": time(), "result": result}, f)
|
2024-12-05 12:53:05 +11:00
|
|
|
|
2024-12-05 12:59:24 +11:00
|
|
|
# Start refresh thread if it doesn't exist
|
|
|
|
refresh_cache(func, cache_file, cache_key, args, kwargs)
|
2024-12-05 12:53:05 +11:00
|
|
|
return result
|
|
|
|
|
|
|
|
return wrapper
|
|
|
|
return decorator
|