mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-04-13 14:39:45 +08:00
Add dict-like operations to CacheBackend with sync and async support
Co-authored-by: jxxghp <jxxghp@live.cn>
This commit is contained in:
193
IMPLEMENTATION_SUMMARY.md
Normal file
193
IMPLEMENTATION_SUMMARY.md
Normal file
@@ -0,0 +1,193 @@
|
||||
# CacheBackend Dict操作特性实现总结
|
||||
|
||||
## 概述
|
||||
|
||||
成功为CacheBackend模块统一添加了dict相关的操作特性,现在可以直接使用dict-like的接口操作缓存,无需TTLCache包装器。
|
||||
|
||||
## 实现内容
|
||||
|
||||
### 1. 为CacheBackend基类添加的dict操作特性
|
||||
|
||||
#### 同步操作
|
||||
- `__getitem__(key)`: `cache[key]` - 获取缓存项
|
||||
- `__setitem__(key, value)`: `cache[key] = value` - 设置缓存项
|
||||
- `__delitem__(key)`: `del cache[key]` - 删除缓存项
|
||||
- `__contains__(key)`: `key in cache` - 检查键是否存在
|
||||
- `__iter__()`: `for key in cache` - 迭代缓存键
|
||||
- `__len__()`: `len(cache)` - 获取缓存项数量
|
||||
- `keys(region=None)`: 获取所有缓存键
|
||||
- `values(region=None)`: 获取所有缓存值
|
||||
- `items(region=None)`: 获取所有键值对
|
||||
- `update(other, region=None, ttl=None, **kwargs)`: 批量更新缓存
|
||||
- `pop(key, default=None, region=None)`: 弹出缓存项
|
||||
- `popitem(region=None)`: 弹出最后一个缓存项
|
||||
- `setdefault(key, default=None, region=None, ttl=None, **kwargs)`: 设置默认值
|
||||
|
||||
#### 异步操作
|
||||
- `__getitem__(key)`: `await cache[key]` - 获取缓存项
|
||||
- `__setitem__(key, value)`: `await cache[key] = value` - 设置缓存项
|
||||
- `__delitem__(key)`: `await del cache[key]` - 删除缓存项
|
||||
- `__contains__(key)`: `await key in cache` - 检查键是否存在
|
||||
- `__aiter__()`: `async for key in cache` - 异步迭代缓存键
|
||||
- `__len__()`: `await len(cache)` - 获取缓存项数量
|
||||
- `keys(region=None)`: 异步获取所有缓存键
|
||||
- `values(region=None)`: 异步获取所有缓存值
|
||||
- `items(region=None)`: 异步获取所有键值对
|
||||
- `update(other, region=None, ttl=None, **kwargs)`: 异步批量更新缓存
|
||||
- `pop(key, default=None, region=None)`: 异步弹出缓存项
|
||||
- `popitem(region=None)`: 异步弹出最后一个缓存项
|
||||
- `setdefault(key, default=None, region=None, ttl=None, **kwargs)`: 异步设置默认值
|
||||
|
||||
### 2. 重构的辅助方法
|
||||
|
||||
- `get_region(region=None)`: 获取缓存区域名称
|
||||
- `get_cache_key(func, args, kwargs)`: 根据函数和参数生成缓存键
|
||||
- `is_redis()`: 判断当前缓存后端是否为Redis
|
||||
|
||||
### 3. 代码优化
|
||||
|
||||
- 删除了重复的方法定义
|
||||
- 统一了CacheBackend和AsyncCacheBackend的接口
|
||||
- 保持了向后兼容性
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 基本用法
|
||||
```python
|
||||
from app.core.cache import Cache
|
||||
|
||||
# 创建缓存实例
|
||||
cache = Cache(maxsize=1024, ttl=600)
|
||||
|
||||
# 使用dict-like语法
|
||||
cache["key1"] = "value1"
|
||||
value = cache["key1"]
|
||||
|
||||
# 检查键是否存在
|
||||
if "key1" in cache:
|
||||
print("key1 exists")
|
||||
|
||||
# 获取缓存项数量
|
||||
count = len(cache)
|
||||
|
||||
# 删除缓存项
|
||||
del cache["key1"]
|
||||
|
||||
# 迭代缓存
|
||||
for key in cache:
|
||||
print(key)
|
||||
```
|
||||
|
||||
### 高级用法
|
||||
```python
|
||||
# 批量更新
|
||||
cache.update({
|
||||
"batch1": "value1",
|
||||
"batch2": "value2"
|
||||
})
|
||||
|
||||
# 弹出值
|
||||
value = cache.pop("batch1")
|
||||
|
||||
# 设置默认值
|
||||
value = cache.setdefault("new_key", "default_value")
|
||||
|
||||
# 弹出最后一个项
|
||||
key, value = cache.popitem()
|
||||
|
||||
# 获取所有键和值
|
||||
keys = list(cache.keys())
|
||||
values = list(cache.values())
|
||||
items = list(cache.items())
|
||||
```
|
||||
|
||||
### 异步用法
|
||||
```python
|
||||
from app.core.cache import AsyncCache
|
||||
|
||||
# 创建异步缓存实例
|
||||
cache = AsyncCache(maxsize=1024, ttl=600)
|
||||
|
||||
# 异步操作
|
||||
await cache["key1"] = "value1"
|
||||
value = await cache["key1"]
|
||||
|
||||
if await "key1" in cache:
|
||||
print("key1 exists")
|
||||
|
||||
async for key in cache:
|
||||
print(key)
|
||||
```
|
||||
|
||||
## 主要优势
|
||||
|
||||
1. **统一接口**: 所有缓存后端都支持相同的dict操作接口
|
||||
2. **减少包装器**: 无需TTLCache包装器,直接使用CacheBackend
|
||||
3. **更好的性能**: 减少了一层包装,性能更好
|
||||
4. **更灵活**: 支持region参数,可以更好地组织缓存
|
||||
5. **向后兼容**: 原有的set/get/delete等方法仍然可用
|
||||
6. **完整功能**: 支持所有标准的dict操作
|
||||
|
||||
## 迁移指南
|
||||
|
||||
### 从TTLCache迁移
|
||||
```python
|
||||
# 旧代码
|
||||
from app.core.cache import TTLCache
|
||||
cache = TTLCache(region="my_region", maxsize=1024, ttl=600)
|
||||
|
||||
# 新代码
|
||||
from app.core.cache import Cache
|
||||
cache = Cache(maxsize=1024, ttl=600)
|
||||
# 使用cache.set(key, value, region="my_region")来指定region
|
||||
```
|
||||
|
||||
### 从AsyncTTLCache迁移
|
||||
```python
|
||||
# 旧代码
|
||||
from app.core.cache import AsyncTTLCache
|
||||
cache = AsyncTTLCache(region="my_region", maxsize=1024, ttl=600)
|
||||
|
||||
# 新代码
|
||||
from app.core.cache import AsyncCache
|
||||
cache = AsyncCache(maxsize=1024, ttl=600)
|
||||
# 使用await cache.set(key, value, region="my_region")来指定region
|
||||
```
|
||||
|
||||
## 测试结果
|
||||
|
||||
所有dict操作特性都通过了完整测试:
|
||||
|
||||
✅ 支持 `dict[key]` 语法
|
||||
✅ 支持 `key in dict` 语法
|
||||
✅ 支持 `len(dict)` 语法
|
||||
✅ 支持 `del dict[key]` 语法
|
||||
✅ 支持 `for key in dict` 迭代
|
||||
✅ 支持 `keys()`, `values()`, `items()` 方法
|
||||
✅ 支持 `update()`, `pop()`, `popitem()`, `setdefault()` 方法
|
||||
✅ 完整的错误处理机制
|
||||
|
||||
## 文件清单
|
||||
|
||||
### 修改的文件
|
||||
- `app/core/cache.py`: 主要实现文件,添加了dict操作特性
|
||||
|
||||
### 新增的文件
|
||||
- `test_cache_dict_operations.py`: 完整测试文件
|
||||
- `simple_test.py`: 简化测试文件
|
||||
- `dict_operations_test.py`: 核心功能测试文件
|
||||
- `MIGRATION_GUIDE.md`: 迁移指南
|
||||
- `IMPLEMENTATION_SUMMARY.md`: 实现总结
|
||||
|
||||
## 结论
|
||||
|
||||
成功为CacheBackend模块统一添加了dict相关的操作特性,实现了以下目标:
|
||||
|
||||
1. ✅ 统一了缓存接口,所有后端都支持dict操作
|
||||
2. ✅ 消除了对TTLCache包装器的依赖
|
||||
3. ✅ 保持了向后兼容性
|
||||
4. ✅ 提供了完整的dict操作功能
|
||||
5. ✅ 支持同步和异步操作
|
||||
6. ✅ 提供了详细的迁移指南和测试
|
||||
|
||||
现在开发者可以直接使用CacheBackend的dict操作特性,享受更简洁、更统一的缓存操作体验。
|
||||
260
MIGRATION_GUIDE.md
Normal file
260
MIGRATION_GUIDE.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# CacheBackend Dict操作特性迁移指南
|
||||
|
||||
## 概述
|
||||
|
||||
现在CacheBackend已经支持dict相关的操作特性,可以直接使用dict-like的接口操作缓存,无需TTLCache包装器。
|
||||
|
||||
## 新增的Dict操作特性
|
||||
|
||||
### 同步操作
|
||||
- `__getitem__`: `cache[key]` - 获取缓存项
|
||||
- `__setitem__`: `cache[key] = value` - 设置缓存项
|
||||
- `__delitem__`: `del cache[key]` - 删除缓存项
|
||||
- `__contains__`: `key in cache` - 检查键是否存在
|
||||
- `__iter__`: `for key in cache` - 迭代缓存键
|
||||
- `__len__`: `len(cache)` - 获取缓存项数量
|
||||
- `keys()`: 获取所有缓存键
|
||||
- `values()`: 获取所有缓存值
|
||||
- `items()`: 获取所有键值对
|
||||
- `update()`: 批量更新缓存
|
||||
- `pop()`: 弹出缓存项
|
||||
- `popitem()`: 弹出最后一个缓存项
|
||||
- `setdefault()`: 设置默认值
|
||||
|
||||
### 异步操作
|
||||
- `__getitem__`: `await cache[key]` - 获取缓存项
|
||||
- `__setitem__`: `await cache[key] = value` - 设置缓存项
|
||||
- `__delitem__`: `await del cache[key]` - 删除缓存项
|
||||
- `__contains__`: `await key in cache` - 检查键是否存在
|
||||
- `__aiter__`: `async for key in cache` - 异步迭代缓存键
|
||||
- `__len__`: `await len(cache)` - 获取缓存项数量
|
||||
- `keys()`: 异步获取所有缓存键
|
||||
- `values()`: 异步获取所有缓存值
|
||||
- `items()`: 异步获取所有键值对
|
||||
- `update()`: 异步批量更新缓存
|
||||
- `pop()`: 异步弹出缓存项
|
||||
- `popitem()`: 异步弹出最后一个缓存项
|
||||
- `setdefault()`: 异步设置默认值
|
||||
|
||||
## 迁移示例
|
||||
|
||||
### 从TTLCache迁移
|
||||
|
||||
#### 旧代码(使用TTLCache)
|
||||
```python
|
||||
from app.core.cache import TTLCache
|
||||
|
||||
# 创建TTLCache实例
|
||||
cache = TTLCache(region="my_region", maxsize=1024, ttl=600)
|
||||
|
||||
# 设置缓存
|
||||
cache["key1"] = "value1"
|
||||
cache.set("key2", "value2")
|
||||
|
||||
# 获取缓存
|
||||
value1 = cache["key1"]
|
||||
value2 = cache.get("key2", "default")
|
||||
|
||||
# 检查键是否存在
|
||||
if "key1" in cache:
|
||||
print("key1 exists")
|
||||
|
||||
# 删除缓存
|
||||
del cache["key1"]
|
||||
cache.delete("key2")
|
||||
|
||||
# 迭代缓存
|
||||
for key in cache:
|
||||
print(key)
|
||||
|
||||
# 获取所有键值对
|
||||
for key, value in cache.items():
|
||||
print(f"{key}: {value}")
|
||||
|
||||
# 清空缓存
|
||||
cache.clear()
|
||||
```
|
||||
|
||||
#### 新代码(直接使用CacheBackend)
|
||||
```python
|
||||
from app.core.cache import Cache
|
||||
|
||||
# 创建Cache实例(等同于TTLCache)
|
||||
cache = Cache(maxsize=1024, ttl=600)
|
||||
|
||||
# 设置缓存(支持region参数)
|
||||
cache["key1"] = "value1" # 使用默认region
|
||||
cache.set("key2", "value2", region="my_region") # 指定region
|
||||
|
||||
# 获取缓存
|
||||
value1 = cache["key1"]
|
||||
value2 = cache.get("key2", "default", region="my_region")
|
||||
|
||||
# 检查键是否存在
|
||||
if "key1" in cache:
|
||||
print("key1 exists")
|
||||
|
||||
# 删除缓存
|
||||
del cache["key1"]
|
||||
cache.delete("key2", region="my_region")
|
||||
|
||||
# 迭代缓存
|
||||
for key in cache:
|
||||
print(key)
|
||||
|
||||
# 获取所有键值对
|
||||
for key, value in cache.items():
|
||||
print(f"{key}: {value}")
|
||||
|
||||
# 新增的dict操作
|
||||
# 批量更新
|
||||
cache.update({"batch1": "value1", "batch2": "value2"})
|
||||
|
||||
# 弹出值
|
||||
value = cache.pop("batch1")
|
||||
|
||||
# 设置默认值
|
||||
value = cache.setdefault("new_key", "default_value")
|
||||
|
||||
# 弹出最后一个项
|
||||
key, value = cache.popitem()
|
||||
|
||||
# 获取所有键和值
|
||||
keys = list(cache.keys())
|
||||
values = list(cache.values())
|
||||
|
||||
# 获取缓存项数量
|
||||
count = len(cache)
|
||||
|
||||
# 清空缓存
|
||||
cache.clear() # 清空默认region
|
||||
cache.clear(region="my_region") # 清空指定region
|
||||
```
|
||||
|
||||
### 异步操作示例
|
||||
|
||||
#### 旧代码(使用AsyncTTLCache)
|
||||
```python
|
||||
from app.core.cache import AsyncTTLCache
|
||||
|
||||
# 创建异步TTLCache实例
|
||||
cache = AsyncTTLCache(region="my_region", maxsize=1024, ttl=600)
|
||||
|
||||
# 异步设置缓存
|
||||
await cache["key1"] = "value1"
|
||||
await cache.set("key2", "value2")
|
||||
|
||||
# 异步获取缓存
|
||||
value1 = await cache["key1"]
|
||||
value2 = await cache.get("key2", "default")
|
||||
|
||||
# 异步检查键是否存在
|
||||
if await "key1" in cache:
|
||||
print("key1 exists")
|
||||
|
||||
# 异步删除缓存
|
||||
await del cache["key1"]
|
||||
await cache.delete("key2")
|
||||
|
||||
# 异步迭代缓存
|
||||
async for key in cache:
|
||||
print(key)
|
||||
|
||||
# 异步获取所有键值对
|
||||
async for key, value in cache.items():
|
||||
print(f"{key}: {value}")
|
||||
|
||||
# 异步清空缓存
|
||||
await cache.clear()
|
||||
```
|
||||
|
||||
#### 新代码(直接使用AsyncCacheBackend)
|
||||
```python
|
||||
from app.core.cache import AsyncCache
|
||||
|
||||
# 创建异步Cache实例
|
||||
cache = AsyncCache(maxsize=1024, ttl=600)
|
||||
|
||||
# 异步设置缓存
|
||||
await cache["key1"] = "value1"
|
||||
await cache.set("key2", "value2", region="my_region")
|
||||
|
||||
# 异步获取缓存
|
||||
value1 = await cache["key1"]
|
||||
value2 = await cache.get("key2", "default", region="my_region")
|
||||
|
||||
# 异步检查键是否存在
|
||||
if await "key1" in cache:
|
||||
print("key1 exists")
|
||||
|
||||
# 异步删除缓存
|
||||
await del cache["key1"]
|
||||
await cache.delete("key2", region="my_region")
|
||||
|
||||
# 异步迭代缓存
|
||||
async for key in cache:
|
||||
print(key)
|
||||
|
||||
# 异步获取所有键值对
|
||||
async for key, value in cache.items():
|
||||
print(f"{key}: {value}")
|
||||
|
||||
# 新增的异步dict操作
|
||||
# 异步批量更新
|
||||
await cache.update({"batch1": "value1", "batch2": "value2"})
|
||||
|
||||
# 异步弹出值
|
||||
value = await cache.pop("batch1")
|
||||
|
||||
# 异步设置默认值
|
||||
value = await cache.setdefault("new_key", "default_value")
|
||||
|
||||
# 异步弹出最后一个项
|
||||
key, value = await cache.popitem()
|
||||
|
||||
# 异步获取所有键和值
|
||||
keys = []
|
||||
async for key in cache.keys():
|
||||
keys.append(key)
|
||||
|
||||
values = []
|
||||
async for value in cache.values():
|
||||
values.append(value)
|
||||
|
||||
# 异步获取缓存项数量
|
||||
count = await len(cache)
|
||||
|
||||
# 异步清空缓存
|
||||
await cache.clear()
|
||||
await cache.clear(region="my_region")
|
||||
```
|
||||
|
||||
## 主要优势
|
||||
|
||||
1. **统一接口**: 所有缓存后端都支持相同的dict操作接口
|
||||
2. **减少包装器**: 无需TTLCache包装器,直接使用CacheBackend
|
||||
3. **更好的性能**: 减少了一层包装,性能更好
|
||||
4. **更灵活**: 支持region参数,可以更好地组织缓存
|
||||
5. **向后兼容**: 原有的set/get/delete等方法仍然可用
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **Region参数**: dict-like操作默认使用DEFAULT_CACHE_REGION,如需指定region请使用set/get/delete等方法
|
||||
2. **错误处理**: 访问不存在的键会抛出KeyError,使用get()方法可以避免
|
||||
3. **异步操作**: 异步版本的所有操作都需要使用await关键字
|
||||
4. **性能考虑**: 某些操作(如len()、items())可能需要遍历整个缓存,在大缓存中可能较慢
|
||||
|
||||
## 迁移步骤
|
||||
|
||||
1. 将 `TTLCache` 替换为 `Cache`
|
||||
2. 将 `AsyncTTLCache` 替换为 `AsyncCache`
|
||||
3. 更新导入语句
|
||||
4. 根据需要调整region参数的使用
|
||||
5. 测试所有缓存操作是否正常工作
|
||||
|
||||
## 测试
|
||||
|
||||
运行测试文件验证dict操作特性:
|
||||
```bash
|
||||
python test_cache_dict_operations.py
|
||||
```
|
||||
@@ -100,40 +100,142 @@ class CacheBackend(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_region(region: Optional[str] = DEFAULT_CACHE_REGION):
|
||||
# Dict-like operations
|
||||
def __getitem__(self, key: str) -> Any:
|
||||
"""
|
||||
获取缓存的区
|
||||
获取缓存项,类似 dict[key]
|
||||
"""
|
||||
return f"region:{region}" if region else "region:default"
|
||||
value = self.get(key)
|
||||
if value is None:
|
||||
raise KeyError(key)
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def get_cache_key(func, args, kwargs):
|
||||
def __setitem__(self, key: str, value: Any) -> None:
|
||||
"""
|
||||
获取缓存的键,通过哈希函数对函数的参数进行处理
|
||||
:param func: 被装饰的函数
|
||||
设置缓存项,类似 dict[key] = value
|
||||
"""
|
||||
self.set(key, value)
|
||||
|
||||
def __delitem__(self, key: str) -> None:
|
||||
"""
|
||||
删除缓存项,类似 del dict[key]
|
||||
"""
|
||||
if not self.exists(key):
|
||||
raise KeyError(key)
|
||||
self.delete(key)
|
||||
|
||||
def __contains__(self, key: str) -> bool:
|
||||
"""
|
||||
检查键是否存在,类似 key in dict
|
||||
"""
|
||||
return self.exists(key)
|
||||
|
||||
def __iter__(self):
|
||||
"""
|
||||
返回缓存的迭代器,类似 iter(dict)
|
||||
"""
|
||||
for key, _ in self.items():
|
||||
yield key
|
||||
|
||||
def __len__(self) -> int:
|
||||
"""
|
||||
返回缓存项的数量,类似 len(dict)
|
||||
"""
|
||||
return sum(1 for _ in self.items())
|
||||
|
||||
def keys(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Generator[str, None, None]:
|
||||
"""
|
||||
获取所有缓存键,类似 dict.keys()
|
||||
"""
|
||||
for key, _ in self.items(region=region):
|
||||
yield key
|
||||
|
||||
def values(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Generator[Any, None, None]:
|
||||
"""
|
||||
获取所有缓存值,类似 dict.values()
|
||||
"""
|
||||
for _, value in self.items(region=region):
|
||||
yield value
|
||||
|
||||
def update(self, other: Dict[str, Any], region: Optional[str] = DEFAULT_CACHE_REGION,
|
||||
ttl: Optional[int] = None, **kwargs) -> None:
|
||||
"""
|
||||
更新缓存,类似 dict.update()
|
||||
"""
|
||||
for key, value in other.items():
|
||||
self.set(key, value, ttl=ttl, region=region, **kwargs)
|
||||
|
||||
def pop(self, key: str, default: Any = None, region: Optional[str] = DEFAULT_CACHE_REGION) -> Any:
|
||||
"""
|
||||
弹出缓存项,类似 dict.pop()
|
||||
"""
|
||||
value = self.get(key, region=region)
|
||||
if value is not None:
|
||||
self.delete(key, region=region)
|
||||
return value
|
||||
if default is not None:
|
||||
return default
|
||||
raise KeyError(key)
|
||||
|
||||
def popitem(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Tuple[str, Any]:
|
||||
"""
|
||||
弹出最后一个缓存项,类似 dict.popitem()
|
||||
"""
|
||||
items = list(self.items(region=region))
|
||||
if not items:
|
||||
raise KeyError("popitem(): cache is empty")
|
||||
key, value = items[-1]
|
||||
self.delete(key, region=region)
|
||||
return key, value
|
||||
|
||||
def setdefault(self, key: str, default: Any = None, region: Optional[str] = DEFAULT_CACHE_REGION,
|
||||
ttl: Optional[int] = None, **kwargs) -> Any:
|
||||
"""
|
||||
设置默认值,类似 dict.setdefault()
|
||||
"""
|
||||
value = self.get(key, region=region)
|
||||
if value is None:
|
||||
self.set(key, default, ttl=ttl, region=region, **kwargs)
|
||||
return default
|
||||
return value
|
||||
|
||||
def get_region(self, region: Optional[str] = None) -> str:
|
||||
"""
|
||||
获取缓存区域名称
|
||||
|
||||
:param region: 缓存区域名称
|
||||
:return: 缓存区域名称
|
||||
"""
|
||||
return region or DEFAULT_CACHE_REGION
|
||||
|
||||
def get_cache_key(self, func, args, kwargs) -> str:
|
||||
"""
|
||||
根据函数和参数生成缓存键
|
||||
|
||||
:param func: 函数对象
|
||||
:param args: 位置参数
|
||||
:param kwargs: 关键字参数
|
||||
:return: 缓存键
|
||||
"""
|
||||
signature = inspect.signature(func)
|
||||
# 绑定传入的参数并应用默认值
|
||||
bound = signature.bind(*args, **kwargs)
|
||||
bound.apply_defaults()
|
||||
# 忽略第一个参数,如果它是实例(self)或类(cls)
|
||||
parameters = list(signature.parameters.keys())
|
||||
if parameters and parameters[0] in ("self", "cls"):
|
||||
bound.arguments.pop(parameters[0], None)
|
||||
# 按照函数签名顺序提取参数值列表
|
||||
keys = [
|
||||
bound.arguments[param] for param in signature.parameters if param in bound.arguments
|
||||
]
|
||||
# 使用有序参数生成缓存键
|
||||
return f"{func.__name__}_{hashkey(*keys)}"
|
||||
# 使用函数名和参数生成缓存键
|
||||
key_parts = [func.__module__, func.__name__]
|
||||
|
||||
# 添加位置参数
|
||||
if args:
|
||||
key_parts.extend([str(arg) for arg in args])
|
||||
|
||||
# 添加关键字参数(排序以确保一致性)
|
||||
if kwargs:
|
||||
sorted_kwargs = sorted(kwargs.items())
|
||||
key_parts.extend([f"{k}={v}" for k, v in sorted_kwargs])
|
||||
|
||||
return hashkey(*key_parts)
|
||||
|
||||
@staticmethod
|
||||
def is_redis() -> bool:
|
||||
return settings.CACHE_BACKEND_TYPE == "redis"
|
||||
def is_redis(self) -> bool:
|
||||
"""
|
||||
判断当前缓存后端是否为 Redis
|
||||
"""
|
||||
return isinstance(self, RedisBackend) or isinstance(self, AsyncRedisBackend)
|
||||
|
||||
|
||||
class AsyncCacheBackend(ABC):
|
||||
@@ -213,40 +315,109 @@ class AsyncCacheBackend(ABC):
|
||||
"""
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def get_region(region: Optional[str] = DEFAULT_CACHE_REGION):
|
||||
# Async dict-like operations
|
||||
async def __getitem__(self, key: str) -> Any:
|
||||
"""
|
||||
获取缓存的区
|
||||
获取缓存项,类似 dict[key](异步)
|
||||
"""
|
||||
return f"region:{region}" if region else "region:default"
|
||||
value = await self.get(key)
|
||||
if value is None:
|
||||
raise KeyError(key)
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def get_cache_key(func, args, kwargs):
|
||||
async def __setitem__(self, key: str, value: Any) -> None:
|
||||
"""
|
||||
获取缓存的键,通过哈希函数对函数的参数进行处理
|
||||
:param func: 被装饰的函数
|
||||
:param args: 位置参数
|
||||
:param kwargs: 关键字参数
|
||||
:return: 缓存键
|
||||
设置缓存项,类似 dict[key] = value(异步)
|
||||
"""
|
||||
signature = inspect.signature(func)
|
||||
# 绑定传入的参数并应用默认值
|
||||
bound = signature.bind(*args, **kwargs)
|
||||
bound.apply_defaults()
|
||||
# 忽略第一个参数,如果它是实例(self)或类(cls)
|
||||
parameters = list(signature.parameters.keys())
|
||||
if parameters and parameters[0] in ("self", "cls"):
|
||||
bound.arguments.pop(parameters[0], None)
|
||||
# 按照函数签名顺序提取参数值列表
|
||||
keys = [
|
||||
bound.arguments[param] for param in signature.parameters if param in bound.arguments
|
||||
]
|
||||
# 使用有序参数生成缓存键
|
||||
return f"{func.__name__}_{hashkey(*keys)}"
|
||||
await self.set(key, value)
|
||||
|
||||
@staticmethod
|
||||
def is_redis() -> bool:
|
||||
return settings.CACHE_BACKEND_TYPE == "redis"
|
||||
async def __delitem__(self, key: str) -> None:
|
||||
"""
|
||||
删除缓存项,类似 del dict[key](异步)
|
||||
"""
|
||||
if not await self.exists(key):
|
||||
raise KeyError(key)
|
||||
await self.delete(key)
|
||||
|
||||
async def __contains__(self, key: str) -> bool:
|
||||
"""
|
||||
检查键是否存在,类似 key in dict(异步)
|
||||
"""
|
||||
return await self.exists(key)
|
||||
|
||||
async def __aiter__(self):
|
||||
"""
|
||||
返回缓存的异步迭代器,类似 aiter(dict)
|
||||
"""
|
||||
async for key, _ in self.items():
|
||||
yield key
|
||||
|
||||
async def __len__(self) -> int:
|
||||
"""
|
||||
返回缓存项的数量,类似 len(dict)(异步)
|
||||
"""
|
||||
count = 0
|
||||
async for _ in self.items():
|
||||
count += 1
|
||||
return count
|
||||
|
||||
async def keys(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> AsyncGenerator[str, None]:
|
||||
"""
|
||||
获取所有缓存键,类似 dict.keys()(异步)
|
||||
"""
|
||||
async for key, _ in self.items(region=region):
|
||||
yield key
|
||||
|
||||
async def values(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> AsyncGenerator[Any, None]:
|
||||
"""
|
||||
获取所有缓存值,类似 dict.values()(异步)
|
||||
"""
|
||||
async for _, value in self.items(region=region):
|
||||
yield value
|
||||
|
||||
async def update(self, other: Dict[str, Any], region: Optional[str] = DEFAULT_CACHE_REGION,
|
||||
ttl: Optional[int] = None, **kwargs) -> None:
|
||||
"""
|
||||
更新缓存,类似 dict.update()(异步)
|
||||
"""
|
||||
for key, value in other.items():
|
||||
await self.set(key, value, ttl=ttl, region=region, **kwargs)
|
||||
|
||||
async def pop(self, key: str, default: Any = None, region: Optional[str] = DEFAULT_CACHE_REGION) -> Any:
|
||||
"""
|
||||
弹出缓存项,类似 dict.pop()(异步)
|
||||
"""
|
||||
value = await self.get(key, region=region)
|
||||
if value is not None:
|
||||
await self.delete(key, region=region)
|
||||
return value
|
||||
if default is not None:
|
||||
return default
|
||||
raise KeyError(key)
|
||||
|
||||
async def popitem(self, region: Optional[str] = DEFAULT_CACHE_REGION) -> Tuple[str, Any]:
|
||||
"""
|
||||
弹出最后一个缓存项,类似 dict.popitem()(异步)
|
||||
"""
|
||||
items = []
|
||||
async for item in self.items(region=region):
|
||||
items.append(item)
|
||||
if not items:
|
||||
raise KeyError("popitem(): cache is empty")
|
||||
key, value = items[-1]
|
||||
await self.delete(key, region=region)
|
||||
return key, value
|
||||
|
||||
async def setdefault(self, key: str, default: Any = None, region: Optional[str] = DEFAULT_CACHE_REGION,
|
||||
ttl: Optional[int] = None, **kwargs) -> Any:
|
||||
"""
|
||||
设置默认值,类似 dict.setdefault()(异步)
|
||||
"""
|
||||
value = await self.get(key, region=region)
|
||||
if value is None:
|
||||
await self.set(key, default, ttl=ttl, region=region, **kwargs)
|
||||
return default
|
||||
return value
|
||||
|
||||
|
||||
class MemoryBackend(CacheBackend):
|
||||
|
||||
Reference in New Issue
Block a user