fastapi-cachekit 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,289 @@
1
+ Metadata-Version: 2.4
2
+ Name: fastapi-cachekit
3
+ Version: 0.1.0
4
+ Summary: High-performance caching solution for FastAPI applications
5
+ Author-email: Bijay Nayak <bijay6779@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/devbijay/fast-cache
8
+ Project-URL: Documentation, https://github.com/devbijay/fast-cache#readme
9
+ Project-URL: Repository, https://github.com/devbijay/fast-cache.git
10
+ Project-URL: Issues, https://github.com/devbijay/fast-cache/issues
11
+ Keywords: fastapi,cache,redis,async,python,starlette,asyncio
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Framework :: FastAPI
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: Internet :: WWW/HTTP
23
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
24
+ Classifier: Topic :: Software Development :: Libraries
25
+ Classifier: Topic :: Utilities
26
+ Classifier: Typing :: Typed
27
+ Requires-Python: >=3.9
28
+ Description-Content-Type: text/markdown
29
+ Requires-Dist: fastapi>=0.75.0
30
+ Requires-Dist: uvicorn[standard]>=0.20.0
31
+ Provides-Extra: redis
32
+ Requires-Dist: redis>=4.2.0; extra == "redis"
33
+ Provides-Extra: postgres
34
+ Requires-Dist: psycopg[pool]>=3.2.9; extra == "postgres"
35
+ Provides-Extra: memcached
36
+ Requires-Dist: aiomcache>=0.8.1; extra == "memcached"
37
+ Requires-Dist: pymemcache>=4.0.0; extra == "memcached"
38
+ Provides-Extra: all
39
+ Requires-Dist: redis>=4.2.0; extra == "all"
40
+ Requires-Dist: psycopg[pool]>=3.2.9; extra == "all"
41
+ Requires-Dist: aiomcache>=0.8.1; extra == "all"
42
+ Requires-Dist: pymemcache>=4.0.0; extra == "all"
43
+
44
+ # fastapi-cachekit
45
+
46
+ A high-performance, flexible caching solution for FastAPI applications. fastapi-cachekit supports both synchronous and asynchronous operations with a clean API and multiple backend options.
47
+
48
+ [![PyPI version](https://badge.fury.io/py/fastapi-cachekit.svg)](https://badge.fury.io/py/fastapi-cachekit)
49
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/release/python-380/)
50
+ [![Downloads](https://pepy.tech/badge/fastapi-cache)](https://pepy.tech/project/fastapi-cachekit)
51
+
52
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
53
+
54
+ ## Features
55
+
56
+ - ✅ Full async/sync support for all operations
57
+ - ✅ Redis backend with connection pooling
58
+ - ✅ Function result caching with decorator syntax
59
+ - ✅ FastAPI dependency injection support
60
+ - ✅ Namespace support for isolating cache entries
61
+ - ✅ Customizable key generation
62
+ - ✅ Type hinting throughout the codebase
63
+ - ✅ Expiration time support (seconds or timedelta)
64
+
65
+ ## 📦 Backends & Sync/Async Support
66
+
67
+ | Backend | Sync API | Async API | Install Extra |
68
+ |--------------------|:--------:|:---------:|----------------------|
69
+ | `InMemoryBackend` | ✅ | ✅ | _built-in_ |
70
+ | `RedisBackend` | ✅ | ✅ | `redis` |
71
+ | `PostgresBackend` | ✅ | ✅ | `postgres` |
72
+ | `MemcachedBackend` | ✅ | ✅ | `memcached` |
73
+
74
+ ---
75
+
76
+ ## 🛠️ Installation
77
+
78
+ **Base (in-memory only):**
79
+ ```bash
80
+ pip install fastapi-cachekit
81
+ ```
82
+
83
+ **With Redis:**
84
+ ```bash
85
+ pip install fastapi-cachekit[redis]
86
+ ```
87
+
88
+ **With Postgres:**
89
+ ```bash
90
+ pip install fastapi-cachekit[postgres]
91
+ ```
92
+
93
+ **With Memcached:**
94
+ ```bash
95
+ pip install fastapi-cachekit[memcached]
96
+ ```
97
+
98
+ **All backends:**
99
+ ```bash
100
+ pip install fastapi-cachekit[all]
101
+ ```
102
+
103
+
104
+ ## Quick Start
105
+
106
+ ```python
107
+ from fastapi import FastAPI, Depends
108
+ from fast_cache import cache, RedisBackend
109
+ from typing import Annotated
110
+
111
+ app = FastAPI()
112
+
113
+ # Initialize cache with Redis backend
114
+ cache.init_app(
115
+ app=app,
116
+ backend=RedisBackend(redis_url="redis://localhost:6379/0", namespace="myapp"),
117
+ default_expire=300 # 5 minutes default expiration
118
+ )
119
+
120
+
121
+ # Use function caching decorator
122
+ @app.get("/items/{item_id}")
123
+ @cache.cached(expire=60) # Cache for 60 seconds
124
+ async def read_item(item_id: int):
125
+ # Expensive operation simulation
126
+ return {"item_id": item_id, "name": f"Item {item_id}"}
127
+
128
+ # Use cache backend directly with dependency injection
129
+ @app.get("/manual-cache")
130
+ async def manual_cache_example(cache_backend: Annotated[RedisBackend, Depends(cache.get_cache)]):
131
+ # Check if key exists
132
+ has_key = await cache_backend.ahas("my-key")
133
+
134
+ if not has_key:
135
+ # Set a value in the cache
136
+ await cache_backend.aset("my-key", {"data": "cached value"}, expire=30)
137
+ return {"cache_set": True}
138
+
139
+ # Get the value from cache
140
+ value = await cache_backend.aget("my-key")
141
+ return {"cached_value": value}
142
+ ```
143
+ Now You Can use the cache from other Sub Routes by importing from
144
+ ```from fast_cache import cache```
145
+
146
+ ## Detailed Usage
147
+
148
+ ### Initializing the Cache
149
+
150
+ Before using the cache, you need to initialize it with a backend:
151
+
152
+ ```python
153
+ from fastapi import FastAPI
154
+ from fast_cache import cache, RedisBackend
155
+ from datetime import timedelta
156
+
157
+ app = FastAPI()
158
+
159
+ cache.init_app(
160
+ app=app,
161
+ backend=RedisBackend(
162
+ redis_url="redis://localhost:6379/0",
163
+ namespace="myapp",
164
+ max_connections=20
165
+ ),
166
+ default_expire=timedelta(minutes=5)
167
+ )
168
+ ```
169
+
170
+ ### Method1: Caching a Function Result Using A Cache Decorator
171
+
172
+ The `@cache.cached()` decorator is the simplest way to cache function results:
173
+
174
+ ```python
175
+ from fast_cache import cache
176
+
177
+ # Cache with default expiration time
178
+ @cache.cached()
179
+ def get_user_data(user_id: int):
180
+ # Expensive database query
181
+ return {"user_id": user_id, "name": "John Doe"}
182
+
183
+ # Cache with custom namespace and expiration
184
+ @cache.cached(namespace="users", expire=300)
185
+ async def get_user_profile(user_id: int):
186
+ # Async expensive operation
187
+ return {"user_id": user_id, "profile": "..."}
188
+
189
+ # Cache with custom key builder
190
+ @cache.cached(key_builder=lambda user_id, **kwargs: f"user:{user_id}")
191
+ def get_user_permissions(user_id: int):
192
+ # Complex permission calculation
193
+ return ["read", "write"]
194
+
195
+ # Skip Cache for Specific Calls
196
+
197
+ Sometimes you need to bypass the cache for certain requests:
198
+
199
+ @cache.cached()
200
+ async def get_weather(city: str, skip_cache: bool = False):
201
+ # Function will be called directly if skip_cache is True
202
+ return await fetch_weather_data(city)
203
+
204
+ # Usage:
205
+ weather = await get_weather("New York", skip_cache=True) # Bypasses cache
206
+ ```
207
+
208
+
209
+ ### Method 2: Using Via Dependency Injection
210
+
211
+ You can access the cache backend directly for more control:
212
+
213
+ ```python
214
+ from fastapi import Depends
215
+ from fast_cache import cache, CacheBackend
216
+ from typing import Annotated
217
+
218
+ @app.get("/api/data")
219
+ async def get_data(cache_backend: Annotated[CacheBackend, Depends(cache.get_cache)]):
220
+ # Try to get from cache
221
+ cached_data = await cache_backend.aget("api:data")
222
+ if cached_data:
223
+ return cached_data
224
+
225
+ # Generate new data
226
+ data = await fetch_expensive_api_data()
227
+
228
+ # Store in cache for 1 hour
229
+ await cache_backend.aset("api:data", data, expire=3600)
230
+
231
+ return data
232
+ ```
233
+
234
+
235
+
236
+ ### Advanced: Implementing Custom Backends
237
+
238
+ You can create your own cache backend by implementing the `CacheBackend` abstract class:
239
+
240
+ ```python
241
+ from fast_cache.backends.backend import CacheBackend
242
+ from typing import Any, Optional, Union
243
+ from datetime import timedelta
244
+
245
+ class MyCustomBackend(CacheBackend):
246
+ # Implement all required methods
247
+ async def aget(self, key: str) -> Any:
248
+ # Your implementation here
249
+ ...
250
+
251
+ def get(self, key: str) -> Any:
252
+ # Your implementation here
253
+ ...
254
+
255
+ # ... implement all other required methods
256
+ ```
257
+
258
+ ## API Reference
259
+
260
+ ### Cache Instance
261
+
262
+ - `cache.init_app(app, backend, default_expire=None)` - Initialize cache with FastAPI app
263
+ - `cache.get_cache()` - Get cache backend instance (for dependency injection)
264
+ - `cache.cached(expire=None, key_builder=None, namespace=None)` - Caching decorator
265
+
266
+ ### CacheBackend Interface
267
+
268
+ All backends implement these methods in both sync and async versions:
269
+
270
+ - `get(key)` / `aget(key)` - Retrieve a value
271
+ - `set(key, value, expire)` / `aset(key, value, expire)` - Store a value
272
+ - `delete(key)` / `adelete(key)` - Delete a value
273
+ - `clear()` / `aclear()` - Clear all values
274
+ - `has(key)` / `ahas(key)` - Check if key exists
275
+
276
+ ### RedisBackend Configuration
277
+
278
+ - `redis_url` - Redis connection string (required)
279
+ - `namespace` - Key prefix (default: "fastapi-cache")
280
+ - `pool_size` - Minimum pool connections (default: 10)
281
+ - `max_connections` - Maximum pool connections (default: 20)
282
+
283
+ ## Contributing
284
+
285
+ Contributions are welcome! Please feel free to submit a Pull Request.
286
+
287
+ ## License
288
+
289
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,246 @@
1
+ # fastapi-cachekit
2
+
3
+ A high-performance, flexible caching solution for FastAPI applications. fastapi-cachekit supports both synchronous and asynchronous operations with a clean API and multiple backend options.
4
+
5
+ [![PyPI version](https://badge.fury.io/py/fastapi-cachekit.svg)](https://badge.fury.io/py/fastapi-cachekit)
6
+ [![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/release/python-380/)
7
+ [![Downloads](https://pepy.tech/badge/fastapi-cache)](https://pepy.tech/project/fastapi-cachekit)
8
+
9
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
+
11
+ ## Features
12
+
13
+ - ✅ Full async/sync support for all operations
14
+ - ✅ Redis backend with connection pooling
15
+ - ✅ Function result caching with decorator syntax
16
+ - ✅ FastAPI dependency injection support
17
+ - ✅ Namespace support for isolating cache entries
18
+ - ✅ Customizable key generation
19
+ - ✅ Type hinting throughout the codebase
20
+ - ✅ Expiration time support (seconds or timedelta)
21
+
22
+ ## 📦 Backends & Sync/Async Support
23
+
24
+ | Backend | Sync API | Async API | Install Extra |
25
+ |--------------------|:--------:|:---------:|----------------------|
26
+ | `InMemoryBackend` | ✅ | ✅ | _built-in_ |
27
+ | `RedisBackend` | ✅ | ✅ | `redis` |
28
+ | `PostgresBackend` | ✅ | ✅ | `postgres` |
29
+ | `MemcachedBackend` | ✅ | ✅ | `memcached` |
30
+
31
+ ---
32
+
33
+ ## 🛠️ Installation
34
+
35
+ **Base (in-memory only):**
36
+ ```bash
37
+ pip install fastapi-cachekit
38
+ ```
39
+
40
+ **With Redis:**
41
+ ```bash
42
+ pip install fastapi-cachekit[redis]
43
+ ```
44
+
45
+ **With Postgres:**
46
+ ```bash
47
+ pip install fastapi-cachekit[postgres]
48
+ ```
49
+
50
+ **With Memcached:**
51
+ ```bash
52
+ pip install fastapi-cachekit[memcached]
53
+ ```
54
+
55
+ **All backends:**
56
+ ```bash
57
+ pip install fastapi-cachekit[all]
58
+ ```
59
+
60
+
61
+ ## Quick Start
62
+
63
+ ```python
64
+ from fastapi import FastAPI, Depends
65
+ from fast_cache import cache, RedisBackend
66
+ from typing import Annotated
67
+
68
+ app = FastAPI()
69
+
70
+ # Initialize cache with Redis backend
71
+ cache.init_app(
72
+ app=app,
73
+ backend=RedisBackend(redis_url="redis://localhost:6379/0", namespace="myapp"),
74
+ default_expire=300 # 5 minutes default expiration
75
+ )
76
+
77
+
78
+ # Use function caching decorator
79
+ @app.get("/items/{item_id}")
80
+ @cache.cached(expire=60) # Cache for 60 seconds
81
+ async def read_item(item_id: int):
82
+ # Expensive operation simulation
83
+ return {"item_id": item_id, "name": f"Item {item_id}"}
84
+
85
+ # Use cache backend directly with dependency injection
86
+ @app.get("/manual-cache")
87
+ async def manual_cache_example(cache_backend: Annotated[RedisBackend, Depends(cache.get_cache)]):
88
+ # Check if key exists
89
+ has_key = await cache_backend.ahas("my-key")
90
+
91
+ if not has_key:
92
+ # Set a value in the cache
93
+ await cache_backend.aset("my-key", {"data": "cached value"}, expire=30)
94
+ return {"cache_set": True}
95
+
96
+ # Get the value from cache
97
+ value = await cache_backend.aget("my-key")
98
+ return {"cached_value": value}
99
+ ```
100
+ Now You Can use the cache from other Sub Routes by importing from
101
+ ```from fast_cache import cache```
102
+
103
+ ## Detailed Usage
104
+
105
+ ### Initializing the Cache
106
+
107
+ Before using the cache, you need to initialize it with a backend:
108
+
109
+ ```python
110
+ from fastapi import FastAPI
111
+ from fast_cache import cache, RedisBackend
112
+ from datetime import timedelta
113
+
114
+ app = FastAPI()
115
+
116
+ cache.init_app(
117
+ app=app,
118
+ backend=RedisBackend(
119
+ redis_url="redis://localhost:6379/0",
120
+ namespace="myapp",
121
+ max_connections=20
122
+ ),
123
+ default_expire=timedelta(minutes=5)
124
+ )
125
+ ```
126
+
127
+ ### Method1: Caching a Function Result Using A Cache Decorator
128
+
129
+ The `@cache.cached()` decorator is the simplest way to cache function results:
130
+
131
+ ```python
132
+ from fast_cache import cache
133
+
134
+ # Cache with default expiration time
135
+ @cache.cached()
136
+ def get_user_data(user_id: int):
137
+ # Expensive database query
138
+ return {"user_id": user_id, "name": "John Doe"}
139
+
140
+ # Cache with custom namespace and expiration
141
+ @cache.cached(namespace="users", expire=300)
142
+ async def get_user_profile(user_id: int):
143
+ # Async expensive operation
144
+ return {"user_id": user_id, "profile": "..."}
145
+
146
+ # Cache with custom key builder
147
+ @cache.cached(key_builder=lambda user_id, **kwargs: f"user:{user_id}")
148
+ def get_user_permissions(user_id: int):
149
+ # Complex permission calculation
150
+ return ["read", "write"]
151
+
152
+ # Skip Cache for Specific Calls
153
+
154
+ Sometimes you need to bypass the cache for certain requests:
155
+
156
+ @cache.cached()
157
+ async def get_weather(city: str, skip_cache: bool = False):
158
+ # Function will be called directly if skip_cache is True
159
+ return await fetch_weather_data(city)
160
+
161
+ # Usage:
162
+ weather = await get_weather("New York", skip_cache=True) # Bypasses cache
163
+ ```
164
+
165
+
166
+ ### Method 2: Using Via Dependency Injection
167
+
168
+ You can access the cache backend directly for more control:
169
+
170
+ ```python
171
+ from fastapi import Depends
172
+ from fast_cache import cache, CacheBackend
173
+ from typing import Annotated
174
+
175
+ @app.get("/api/data")
176
+ async def get_data(cache_backend: Annotated[CacheBackend, Depends(cache.get_cache)]):
177
+ # Try to get from cache
178
+ cached_data = await cache_backend.aget("api:data")
179
+ if cached_data:
180
+ return cached_data
181
+
182
+ # Generate new data
183
+ data = await fetch_expensive_api_data()
184
+
185
+ # Store in cache for 1 hour
186
+ await cache_backend.aset("api:data", data, expire=3600)
187
+
188
+ return data
189
+ ```
190
+
191
+
192
+
193
+ ### Advanced: Implementing Custom Backends
194
+
195
+ You can create your own cache backend by implementing the `CacheBackend` abstract class:
196
+
197
+ ```python
198
+ from fast_cache.backends.backend import CacheBackend
199
+ from typing import Any, Optional, Union
200
+ from datetime import timedelta
201
+
202
+ class MyCustomBackend(CacheBackend):
203
+ # Implement all required methods
204
+ async def aget(self, key: str) -> Any:
205
+ # Your implementation here
206
+ ...
207
+
208
+ def get(self, key: str) -> Any:
209
+ # Your implementation here
210
+ ...
211
+
212
+ # ... implement all other required methods
213
+ ```
214
+
215
+ ## API Reference
216
+
217
+ ### Cache Instance
218
+
219
+ - `cache.init_app(app, backend, default_expire=None)` - Initialize cache with FastAPI app
220
+ - `cache.get_cache()` - Get cache backend instance (for dependency injection)
221
+ - `cache.cached(expire=None, key_builder=None, namespace=None)` - Caching decorator
222
+
223
+ ### CacheBackend Interface
224
+
225
+ All backends implement these methods in both sync and async versions:
226
+
227
+ - `get(key)` / `aget(key)` - Retrieve a value
228
+ - `set(key, value, expire)` / `aset(key, value, expire)` - Store a value
229
+ - `delete(key)` / `adelete(key)` - Delete a value
230
+ - `clear()` / `aclear()` - Clear all values
231
+ - `has(key)` / `ahas(key)` - Check if key exists
232
+
233
+ ### RedisBackend Configuration
234
+
235
+ - `redis_url` - Redis connection string (required)
236
+ - `namespace` - Key prefix (default: "fastapi-cache")
237
+ - `pool_size` - Minimum pool connections (default: 10)
238
+ - `max_connections` - Maximum pool connections (default: 20)
239
+
240
+ ## Contributing
241
+
242
+ Contributions are welcome! Please feel free to submit a Pull Request.
243
+
244
+ ## License
245
+
246
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,13 @@
1
+ from .integration import FastAPICache
2
+ from .backends.backend import CacheBackend
3
+
4
+ from .backends.redis import RedisBackend
5
+ from .backends.memory import InMemoryBackend
6
+ from .backends.postgres import PostgresBackend
7
+ from .backends.memcached import MemcachedBackend
8
+
9
+ __all__ = ["FastAPICache", "RedisBackend", "CacheBackend", "InMemoryBackend","PostgresBackend", "cache","MemcachedBackend" ]
10
+
11
+
12
+ # Create global cache instance
13
+ cache = FastAPICache()
File without changes
@@ -0,0 +1,126 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import Any, Optional, Union
3
+ from datetime import timedelta
4
+
5
+
6
+ class CacheBackend(ABC):
7
+ """
8
+ Abstract base class for cache backends.
9
+
10
+ All cache backend implementations must inherit from this class and implement
11
+ both synchronous and asynchronous methods for cache operations.
12
+ """
13
+
14
+ @abstractmethod
15
+ async def aget(self, key: str) -> Optional[Any]:
16
+ """
17
+ Asynchronously retrieve a value from the cache.
18
+
19
+ Args:
20
+ key (str): The key to retrieve.
21
+
22
+ Returns:
23
+ Optional[Any]: The cached value, or None if not found.
24
+ """
25
+ pass
26
+
27
+ @abstractmethod
28
+ def get(self, key: str) -> Optional[Any]:
29
+ """
30
+ Synchronously retrieve a value from the cache.
31
+
32
+ Args:
33
+ key (str): The key to retrieve.
34
+
35
+ Returns:
36
+ Optional[Any]: The cached value, or None if not found.
37
+ """
38
+ pass
39
+
40
+ @abstractmethod
41
+ async def aset(
42
+ self, key: str, value: Any, expire: Optional[Union[int, timedelta]] = None
43
+ ) -> None:
44
+ """
45
+ Asynchronously set a value in the cache.
46
+
47
+ Args:
48
+ key (str): The key under which to store the value.
49
+ value (Any): The value to store.
50
+ expire (Optional[Union[int, timedelta]]): Expiration time in seconds or as timedelta.
51
+ """
52
+ pass
53
+
54
+ @abstractmethod
55
+ def set(
56
+ self, key: str, value: Any, expire: Optional[Union[int, timedelta]] = None
57
+ ) -> None:
58
+ """
59
+ Synchronously set a value in the cache.
60
+
61
+ Args:
62
+ key (str): The key under which to store the value.
63
+ value (Any): The value to store.
64
+ expire (Optional[Union[int, timedelta]]): Expiration time in seconds or as timedelta.
65
+ """
66
+ pass
67
+
68
+ @abstractmethod
69
+ async def adelete(self, key: str) -> None:
70
+ """
71
+ Asynchronously delete a value from the cache.
72
+
73
+ Args:
74
+ key (str): The key to delete.
75
+ """
76
+ pass
77
+
78
+ @abstractmethod
79
+ def delete(self, key: str) -> None:
80
+ """
81
+ Synchronously delete a value from the cache.
82
+
83
+ Args:
84
+ key (str): The key to delete.
85
+ """
86
+ pass
87
+
88
+ @abstractmethod
89
+ async def aclear(self) -> None:
90
+ """
91
+ Asynchronously clear all values from the cache.
92
+ """
93
+ pass
94
+
95
+ @abstractmethod
96
+ def clear(self) -> None:
97
+ """
98
+ Synchronously clear all values from the cache.
99
+ """
100
+ pass
101
+
102
+ @abstractmethod
103
+ async def ahas(self, key: str) -> bool:
104
+ """
105
+ Asynchronously check if a key exists in the cache.
106
+
107
+ Args:
108
+ key (str): The key to check.
109
+
110
+ Returns:
111
+ bool: True if the key exists, False otherwise.
112
+ """
113
+ pass
114
+
115
+ @abstractmethod
116
+ def has(self, key: str) -> bool:
117
+ """
118
+ Synchronously check if a key exists in the cache.
119
+
120
+ Args:
121
+ key (str): The key to check.
122
+
123
+ Returns:
124
+ bool: True if the key exists, False otherwise.
125
+ """
126
+ pass