Skip to content

Commit 652a497

Browse files
committed
first working basic expires decorator
Signed-off-by: Grant Ramsay <seapagan@gmail.com>
1 parent bfef247 commit 652a497

File tree

4 files changed

+45
-12
lines changed

4 files changed

+45
-12
lines changed

fastapi_redis_cache/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
cache_one_month,
99
cache_one_week,
1010
cache_one_year,
11+
expires,
1112
)
1213
from fastapi_redis_cache.client import FastApiRedisCache
1314

@@ -19,5 +20,6 @@
1920
"cache_one_month",
2021
"cache_one_week",
2122
"cache_one_year",
23+
"expires",
2224
"FastApiRedisCache",
2325
]

fastapi_redis_cache/cache.py

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -127,13 +127,19 @@ async def inner_wrapper(
127127
return outer_wrapper
128128

129129

130-
def expires(tag: str | None = None) -> Callable[..., Any]:
130+
def expires(
131+
tag: str | None = None,
132+
arg: str | None = None, # noqa: ARG001
133+
) -> Callable[..., Any]:
131134
"""Invalidate all cached responses with the same tag.
132135
133136
Args:
134-
tag (str, optional): A tag to associate with the cached response. This
135-
can later be used to invalidate all cached responses with the same
136-
tag, or for further fine-grained cache expiry. Defaults to None.
137+
tag (str, optional): The tag to search for keys to expire.
138+
Defaults to None.
139+
arg: (str, optional): The function arguement to filter for expiry. This
140+
would generally be the varying arguement suppplied to the route.
141+
Defaults to None. If not specified, the kwargs for the route will
142+
be used to search for the key to expire.
137143
"""
138144

139145
def outer_wrapper(func: Callable[..., Any]) -> Callable[..., Any]:
@@ -144,14 +150,26 @@ async def inner_wrapper(
144150
) -> Any: # noqa: ANN401
145151
"""Invalidate all cached responses with the same tag."""
146152
redis_cache = FastApiRedisCache()
147-
if redis_cache.not_connected:
148-
return await get_api_response_async(func, *args, **kwargs)
149-
if tag:
150-
# expire all keys with the same tag. This is a test we will
151-
# later only expire keys that have the search argument in the
152-
# key.
153+
orig_response = await get_api_response_async(func, *args, **kwargs)
154+
155+
if not redis_cache.redis or not redis_cache.connected or not tag:
156+
# we only want to invalidate the cache if the redis client is
157+
# connected and a tag is provided.
158+
return orig_response
159+
if kwargs:
160+
search = "".join(
161+
[f"({key}={value})" for key, value in kwargs.items()]
162+
)
163+
tag_keys = redis_cache.get_tagged_keys(tag)
164+
found_keys = [key for key in tag_keys if search.encode() in key]
165+
for key in found_keys:
166+
redis_cache.redis.delete(key)
167+
redis_cache.redis.srem(tag, key)
168+
else:
169+
# will fill this later, what to do if no kwargs are provided
153170
pass
154-
return await get_api_response_async(func, *args, **kwargs)
171+
172+
return orig_response
155173

156174
return inner_wrapper
157175

fastapi_redis_cache/client.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from fastapi_redis_cache.util import serialize_json
2121

2222
if TYPE_CHECKING: # pragma: no cover
23+
from collections.abc import ByteString
24+
2325
from fastapi import Request, Response
2426
from redis import client
2527

@@ -158,7 +160,7 @@ def add_key_to_tag_set(self, tag: str, key: str) -> None:
158160
if self.redis:
159161
self.redis.sadd(tag, key)
160162

161-
def get_tagged_keys(self, tag: str) -> set[str]:
163+
def get_tagged_keys(self, tag: str) -> set[ByteString]:
162164
"""Return a set of keys associated with a tag."""
163165
return self.redis.smembers(tag) if self.redis else set()
164166

tests/live_test.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
cache,
2020
cache_one_hour,
2121
cache_one_minute,
22+
expires,
2223
)
2324

2425
REDIS_SERVER_URL = "redis://127.0.0.1:"
@@ -104,3 +105,13 @@ def cache_with_args(user: int) -> dict[str, Union[bool, str]]:
104105
"success": True,
105106
"message": f"this data is for user {user}",
106107
}
108+
109+
110+
@app.put("/cache_with_args/{user}")
111+
@expires(tag="user_tag")
112+
def put_cache_with_args(user: int) -> dict[str, Union[bool, str]]:
113+
"""Put request to change data for a specific user."""
114+
return {
115+
"success": True,
116+
"message": f"New data for User {user}",
117+
}

0 commit comments

Comments
 (0)