fastapi-cachekit 0.1.3__tar.gz → 0.1.4__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.
Files changed (21) hide show
  1. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/PKG-INFO +12 -1
  2. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/README.md +6 -0
  3. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fast_cache/__init__.py +3 -1
  4. fastapi_cachekit-0.1.4/fast_cache/backends/dynamodb.py +495 -0
  5. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fast_cache/backends/postgres.py +10 -13
  6. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fastapi_cachekit.egg-info/PKG-INFO +12 -1
  7. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fastapi_cachekit.egg-info/SOURCES.txt +1 -0
  8. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fastapi_cachekit.egg-info/requires.txt +6 -0
  9. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/pyproject.toml +8 -2
  10. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/LICENSE.md +0 -0
  11. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fast_cache/backends/__init__.py +0 -0
  12. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fast_cache/backends/backend.py +0 -0
  13. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fast_cache/backends/google_firestore.py +0 -0
  14. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fast_cache/backends/memcached.py +0 -0
  15. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fast_cache/backends/memory.py +0 -0
  16. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fast_cache/backends/mongodb.py +0 -0
  17. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fast_cache/backends/redis.py +0 -0
  18. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fast_cache/integration.py +0 -0
  19. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fastapi_cachekit.egg-info/dependency_links.txt +0 -0
  20. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/fastapi_cachekit.egg-info/top_level.txt +0 -0
  21. {fastapi_cachekit-0.1.3 → fastapi_cachekit-0.1.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-cachekit
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: High-performance caching solution for FastAPI applications
5
5
  Author-email: Bijay Nayak <bijay6779@gmail.com>
6
6
  License-Expression: MIT
@@ -39,6 +39,9 @@ Provides-Extra: mongodb
39
39
  Requires-Dist: pymongo[gssapi,snappy,srv]>=4.6.0; extra == "mongodb"
40
40
  Provides-Extra: firestore
41
41
  Requires-Dist: google-cloud-firestore>=2.3.0; extra == "firestore"
42
+ Provides-Extra: dynamodb
43
+ Requires-Dist: boto3>=1.10.0; extra == "dynamodb"
44
+ Requires-Dist: aioboto3>=6.0.0; extra == "dynamodb"
42
45
  Provides-Extra: all
43
46
  Requires-Dist: redis>=4.2.0; extra == "all"
44
47
  Requires-Dist: psycopg[pool]>=3.2.9; extra == "all"
@@ -46,6 +49,8 @@ Requires-Dist: aiomcache>=0.8.1; extra == "all"
46
49
  Requires-Dist: pymemcache>=4.0.0; extra == "all"
47
50
  Requires-Dist: pymongo[gssapi,snappy,srv]>=4.6.0; extra == "all"
48
51
  Requires-Dist: google-cloud-firestore>=2.3.0; extra == "all"
52
+ Requires-Dist: boto3>=1.10.0; extra == "all"
53
+ Requires-Dist: aioboto3>=6.0.0; extra == "all"
49
54
  Dynamic: license-file
50
55
 
51
56
  # fastapi-cachekit
@@ -78,6 +83,7 @@ A high-performance, flexible caching solution for FastAPI applications. fastapi-
78
83
  | `MemcachedBackend` | ✅ | ✅ | `memcached` |
79
84
  | `MongoDB` | ✅ | ✅ | `mongodb` |
80
85
  | `FireStore` | ✅ | ✅ | `firestore` |
86
+ | `DynamoDBBackend` | ✅ | ✅ | `dynamodb` |
81
87
 
82
88
  ---
83
89
 
@@ -112,6 +118,11 @@ pip install fastapi-cachekit[mongodb]
112
118
  pip install fastapi-cachekit[firestore]
113
119
  ```
114
120
 
121
+ **With DynamoDB:**
122
+ ```bash
123
+ pip install fastapi-cachekit[dynamodb]
124
+ ```
125
+
115
126
  **All backends:**
116
127
  ```bash
117
128
  pip install fastapi-cachekit[all]
@@ -28,6 +28,7 @@ A high-performance, flexible caching solution for FastAPI applications. fastapi-
28
28
  | `MemcachedBackend` | ✅ | ✅ | `memcached` |
29
29
  | `MongoDB` | ✅ | ✅ | `mongodb` |
30
30
  | `FireStore` | ✅ | ✅ | `firestore` |
31
+ | `DynamoDBBackend` | ✅ | ✅ | `dynamodb` |
31
32
 
32
33
  ---
33
34
 
@@ -62,6 +63,11 @@ pip install fastapi-cachekit[mongodb]
62
63
  pip install fastapi-cachekit[firestore]
63
64
  ```
64
65
 
66
+ **With DynamoDB:**
67
+ ```bash
68
+ pip install fastapi-cachekit[dynamodb]
69
+ ```
70
+
65
71
  **All backends:**
66
72
  ```bash
67
73
  pip install fastapi-cachekit[all]
@@ -7,6 +7,7 @@ from .backends.postgres import PostgresBackend
7
7
  from .backends.memcached import MemcachedBackend
8
8
  from .backends.mongodb import MongoDBBackend
9
9
  from .backends.google_firestore import FirestoreBackend
10
+ from .backends.dynamodb import DynamoDBBackend
10
11
 
11
12
  __all__ = [
12
13
  "FastAPICache",
@@ -17,7 +18,8 @@ __all__ = [
17
18
  "cache",
18
19
  "MemcachedBackend",
19
20
  "MongoDBBackend",
20
- "FirestoreBackend"
21
+ "FirestoreBackend",
22
+ "DynamoDBBackend"
21
23
  ]
22
24
 
23
25
 
@@ -0,0 +1,495 @@
1
+ import hashlib
2
+ from typing import Any, Optional, Union
3
+ from datetime import timedelta
4
+ import pickle
5
+ import time
6
+
7
+ from .backend import CacheBackend
8
+
9
+
10
+ class DynamoDBBackend(CacheBackend):
11
+ """
12
+ DynamoDB cache backend implementation with namespace support.
13
+
14
+ Attributes:
15
+ _namespace (str): Namespace prefix for all keys.
16
+ _table_name (str): DynamoDB table name.
17
+ _sync_client (boto3.client): Synchronous DynamoDB client.
18
+ _async_client (aioboto3.client): Asynchronous DynamoDB client.
19
+ _sync_resource (boto3.resource): Synchronous DynamoDB resource.
20
+ _async_resource (aioboto3.resource): Asynchronous DynamoDB resource.
21
+ """
22
+
23
+ def __init__(
24
+ self,
25
+ table_name: str,
26
+ region_name: str,
27
+ namespace: str = "cache",
28
+ aws_access_key_id: Optional[str] = None,
29
+ aws_secret_access_key: Optional[str] = None,
30
+ endpoint_url: Optional[str] = None,
31
+ create_table: bool = True,
32
+ ) -> None:
33
+ """
34
+ Initialize DynamoDB backend with table and connection settings.
35
+
36
+ Args:
37
+ table_name (str): DynamoDB table name for cache storage.
38
+ namespace (str): Namespace prefix for all keys (default: "fastapi-cache").
39
+ region_name (str): AWS region name (default: "us-east-1").
40
+ aws_access_key_id (Optional[str]): AWS access key ID.
41
+ aws_secret_access_key (Optional[str]): AWS secret access key.
42
+ endpoint_url (Optional[str]): Custom endpoint URL (for local DynamoDB).
43
+ create_table (bool): Whether to create table if it doesn't exist.
44
+ """
45
+ try:
46
+ import boto3
47
+ import aioboto3
48
+ except ImportError:
49
+ raise ImportError(
50
+ "DynamoDBBackend requires the 'boto3' and 'aioboto3' packages. "
51
+ "Install them with: pip install fast-cache[dynamodb]"
52
+ )
53
+
54
+ self._namespace = namespace
55
+ self._table_name = table_name
56
+
57
+ # Connection parameters
58
+ self._connection_params = {
59
+ "region_name": region_name,
60
+ "endpoint_url": endpoint_url,
61
+ }
62
+
63
+ if aws_access_key_id and aws_secret_access_key:
64
+ self._connection_params.update(
65
+ {
66
+ "aws_access_key_id": aws_access_key_id,
67
+ "aws_secret_access_key": aws_secret_access_key,
68
+ }
69
+ )
70
+
71
+ # Sync client for table management only
72
+ self._sync_client = boto3.client("dynamodb", **self._connection_params)
73
+
74
+ # Sync resource/table for sync cache operations
75
+ self._sync_resource = boto3.resource("dynamodb", **self._connection_params)
76
+ self._sync_table = self._sync_resource.Table(table_name)
77
+
78
+ # Initialize async session
79
+ self._async_resource = None
80
+ self._async_table = None
81
+ self._async_session = aioboto3.Session()
82
+
83
+ # Create table if requested
84
+ if create_table:
85
+ self._ensure_table_exists()
86
+
87
+ async def _get_async_table(self):
88
+ if self._async_table is None:
89
+ # Create the resource context
90
+ self._async_resource = self._async_session.resource(
91
+ "dynamodb", **self._connection_params
92
+ )
93
+
94
+ # Enter the context and get the actual resource
95
+ actual_resource = await self._async_resource.__aenter__()
96
+
97
+ # Create the table from the actual resource
98
+ self._async_table = await actual_resource.Table(self._table_name)
99
+
100
+
101
+ return self._async_table
102
+
103
+ def _ensure_table_exists(self) -> None:
104
+ """
105
+ Ensure the DynamoDB table exists, create if it doesn't.
106
+ """
107
+
108
+ try:
109
+ self._sync_client.describe_table(TableName=self._table_name)
110
+ except self._sync_client.exceptions.ResourceNotFoundException:
111
+ # Table doesn't exist, create it
112
+ self._sync_client.create_table(
113
+ TableName=self._table_name,
114
+ KeySchema=[{"AttributeName": "cache_key", "KeyType": "HASH"}],
115
+ AttributeDefinitions=[
116
+ {"AttributeName": "cache_key", "AttributeType": "S"}
117
+ ],
118
+ BillingMode="PAY_PER_REQUEST",
119
+ )
120
+
121
+ # Wait for table to be created
122
+ waiter = self._sync_client.get_waiter("table_exists")
123
+ waiter.wait(TableName=self._table_name)
124
+
125
+ try:
126
+ self._sync_client.update_time_to_live(
127
+ TableName=self._table_name,
128
+ TimeToLiveSpecification={"Enabled": True, "AttributeName": "ttl"},
129
+ )
130
+ except Exception:
131
+ pass
132
+
133
+ def _make_key(self, key: str) -> str:
134
+ """
135
+ Create a namespaced key with optional hashing for long keys.
136
+
137
+ Args:
138
+ key (str): The original key.
139
+
140
+ Returns:
141
+ str: The namespaced key, hashed if too long.
142
+ """
143
+ namespaced_key = f"{self._namespace}:{key}"
144
+
145
+ # DynamoDB has a 2KB limit for partition keys
146
+ if len(namespaced_key.encode("utf-8")) > 1024:
147
+ key_hash = hashlib.sha256(key.encode("utf-8")).hexdigest()
148
+ namespaced_key = f"{self._namespace}:hash:{key_hash}"
149
+
150
+ return namespaced_key
151
+
152
+ def _get_ttl(self, expire: Optional[Union[int, timedelta]]) -> Optional[int]:
153
+ """
154
+ Calculate TTL timestamp for DynamoDB.
155
+
156
+ Args:
157
+ expire (Optional[Union[int, timedelta]]): Expiration time.
158
+
159
+ Returns:
160
+ Optional[int]: TTL timestamp or None if no expiration.
161
+ """
162
+ if expire is None:
163
+ return None
164
+
165
+ if isinstance(expire, timedelta):
166
+ expire = int(expire.total_seconds())
167
+
168
+ if expire <= 0:
169
+ return None
170
+
171
+ return int(time.time()) + expire
172
+
173
+ def _is_expired(self, item: dict) -> bool:
174
+ """
175
+ Check if an item has expired based on TTL.
176
+
177
+ Args:
178
+ item (dict): DynamoDB item.
179
+
180
+ Returns:
181
+ bool: True if expired, False otherwise.
182
+ """
183
+ if "ttl" not in item:
184
+ return False
185
+
186
+ return time.time() > item["ttl"]
187
+
188
+ def _serialize_value(self, value: Any) -> bytes:
189
+ """
190
+ Serialize value for storage in DynamoDB.
191
+
192
+ Args:
193
+ value (Any): Value to serialize.
194
+
195
+ Returns:
196
+ bytes: Serialized value.
197
+ """
198
+ return pickle.dumps(value, protocol=pickle.HIGHEST_PROTOCOL)
199
+
200
+ def _deserialize_value(self, data: bytes) -> Any:
201
+ """
202
+ Deserialize value from DynamoDB storage.
203
+
204
+ Args:
205
+ data (bytes): Serialized data.
206
+
207
+ Returns:
208
+ Any: Deserialized value.
209
+ """
210
+ return pickle.loads(bytes(data))
211
+
212
+ def _build_item(
213
+ self, key: str, value: Any, expire: Optional[Union[int, timedelta]] = None
214
+ ) -> dict:
215
+ """
216
+ Build a DynamoDB item for storage.
217
+
218
+ Args:
219
+ key (str): Cache key.
220
+ value (Any): Value to store.
221
+ expire (Optional[Union[int, timedelta]]): Expiration time.
222
+
223
+ Returns:
224
+ dict: DynamoDB item.
225
+ """
226
+ item = {
227
+ "cache_key": self._make_key(key),
228
+ "value": self._serialize_value(value),
229
+ }
230
+
231
+ ttl = self._get_ttl(expire)
232
+ if ttl is not None:
233
+ item["ttl"] = ttl
234
+
235
+ return item
236
+
237
+ def get(self, key: str) -> Optional[Any]:
238
+ """
239
+ Synchronously retrieve a value from the cache.
240
+
241
+ Args:
242
+ key (str): The key to retrieve.
243
+
244
+ Returns:
245
+ Optional[Any]: The cached value, or None if not found.
246
+ """
247
+ try:
248
+ response = self._sync_table.get_item(Key={"cache_key": self._make_key(key)})
249
+
250
+ if "Item" not in response:
251
+ return None
252
+
253
+ item = response["Item"]
254
+
255
+ # Check if item has expired and delete if so
256
+ if self._is_expired(item):
257
+ self.delete(key)
258
+ return None
259
+ value = self._deserialize_value(item["value"])
260
+ return value
261
+ except Exception:
262
+ return None
263
+
264
+ async def aget(self, key: str) -> Optional[Any]:
265
+ """
266
+ Asynchronously retrieve a value from the cache.
267
+
268
+ Args:
269
+ key (str): The key to retrieve.
270
+
271
+ Returns:
272
+ Optional[Any]: The cached value, or None if not found.
273
+ """
274
+ try:
275
+ table = await self._get_async_table()
276
+ response = await table.get_item(Key={"cache_key": self._make_key(key)})
277
+
278
+ if "Item" not in response:
279
+ return None
280
+
281
+ item = response["Item"]
282
+
283
+ # Check if item has expired and delete if so
284
+ if self._is_expired(item):
285
+ await self.adelete(key)
286
+ return None
287
+
288
+ return self._deserialize_value(item["value"])
289
+ except Exception:
290
+ return None
291
+
292
+ def set(
293
+ self, key: str, value: Any, expire: Optional[Union[int, timedelta]] = None
294
+ ) -> None:
295
+ """
296
+ Synchronously set a value in the cache.
297
+
298
+ Args:
299
+ key (str): The key under which to store the value.
300
+ value (Any): The value to store.
301
+ expire (Optional[Union[int, timedelta]]): Expiration time in seconds or as timedelta.
302
+ """
303
+ try:
304
+ item = self._build_item(key, value, expire)
305
+ self._sync_table.put_item(Item=item)
306
+ except Exception:
307
+ pass
308
+
309
+ async def aset(
310
+ self, key: str, value: Any, expire: Optional[Union[int, timedelta]] = None
311
+ ) -> None:
312
+ """
313
+ Asynchronously set a value in the cache.
314
+
315
+ Args:
316
+ key (str): The key under which to store the value.
317
+ value (Any): The value to store.
318
+ expire (Optional[Union[int, timedelta]]): Expiration time in seconds or as timedelta.
319
+ """
320
+ try:
321
+ table = await self._get_async_table()
322
+ item = self._build_item(key, value, expire)
323
+ await table.put_item(Item=item)
324
+ except Exception:
325
+ pass
326
+
327
+ def delete(self, key: str) -> None:
328
+ """
329
+ Synchronously delete a value from the cache.
330
+
331
+ Args:
332
+ key (str): The key to delete.
333
+ """
334
+ try:
335
+ self._sync_table.delete_item(Key={"cache_key": self._make_key(key)})
336
+ except Exception:
337
+ pass
338
+
339
+ async def adelete(self, key: str) -> None:
340
+ """
341
+ Asynchronously delete a value from the cache.
342
+
343
+ Args:
344
+ key (str): The key to delete.
345
+ """
346
+ try:
347
+ table = await self._get_async_table()
348
+ await table.delete_item(Key={"cache_key": self._make_key(key)})
349
+ except Exception:
350
+ pass
351
+
352
+ def has(self, key: str) -> bool:
353
+ """
354
+ Synchronously check if a key exists in the cache.
355
+
356
+ Args:
357
+ key (str): The key to check.
358
+
359
+ Returns:
360
+ bool: True if the key exists, False otherwise.
361
+ """
362
+ try:
363
+ response = self._sync_table.get_item(
364
+ Key={"cache_key": self._make_key(key)},
365
+ ProjectionExpression="cache_key, #ttl",
366
+ ExpressionAttributeNames={"#ttl": "ttl"},
367
+ )
368
+
369
+
370
+ if "Item" not in response:
371
+ return False
372
+
373
+ item = response["Item"]
374
+
375
+ # Check if item has expired and delete if so
376
+ if self._is_expired(item):
377
+ self.delete(key)
378
+ return False
379
+
380
+ return True
381
+ except Exception:
382
+ return False
383
+
384
+ async def ahas(self, key: str) -> bool:
385
+ """
386
+ Asynchronously check if a key exists in the cache.
387
+
388
+ Args:
389
+ key (str): The key to check.
390
+
391
+ Returns:
392
+ bool: True if the key exists, False otherwise.
393
+ """
394
+ try:
395
+ table = await self._get_async_table()
396
+ response = await table.get_item(
397
+ Key={"cache_key": self._make_key(key)},
398
+ ProjectionExpression="cache_key, #ttl",
399
+ ExpressionAttributeNames={"#ttl": "ttl"},
400
+ )
401
+
402
+ if "Item" not in response:
403
+ return False
404
+
405
+ item = response["Item"]
406
+
407
+ # Check if item has expired and delete if so
408
+ if self._is_expired(item):
409
+ await self.adelete(key)
410
+ return False
411
+
412
+ return True
413
+ except Exception:
414
+ return False
415
+
416
+ def clear(self) -> None:
417
+ """
418
+ Synchronously clear all values from the namespace.
419
+ """
420
+ try:
421
+ # Scan for all items with the namespace prefix
422
+ response = self._sync_table.scan(
423
+ FilterExpression="begins_with(cache_key, :prefix)",
424
+ ExpressionAttributeValues={":prefix": f"{self._namespace}:"},
425
+ ProjectionExpression="cache_key",
426
+ )
427
+
428
+ # Delete items in batches
429
+ if response.get("Items"):
430
+ with self._sync_table.batch_writer() as batch:
431
+ for item in response["Items"]:
432
+ batch.delete_item(Key={"cache_key": item["cache_key"]})
433
+
434
+ # Handle pagination
435
+ while "LastEvaluatedKey" in response:
436
+ response = self._sync_table.scan(
437
+ FilterExpression="begins_with(cache_key, :prefix)",
438
+ ExpressionAttributeValues={":prefix": f"{self._namespace}:"},
439
+ ProjectionExpression="cache_key",
440
+ ExclusiveStartKey=response["LastEvaluatedKey"],
441
+ )
442
+
443
+ if response.get("Items"):
444
+ with self._sync_table.batch_writer() as batch:
445
+ for item in response["Items"]:
446
+ batch.delete_item(Key={"cache_key": item["cache_key"]})
447
+
448
+ except Exception:
449
+ pass
450
+
451
+ async def aclear(self) -> None:
452
+ """
453
+ Asynchronously clear all values from the namespace.
454
+ """
455
+ try:
456
+ table = await self._get_async_table()
457
+
458
+ # Scan for all items with the namespace prefix
459
+ response = await table.scan(
460
+ FilterExpression="begins_with(cache_key, :prefix)",
461
+ ExpressionAttributeValues={":prefix": f"{self._namespace}:"},
462
+ ProjectionExpression="cache_key",
463
+ )
464
+
465
+ # Delete items in batches
466
+ if response.get("Items"):
467
+ async with table.batch_writer() as batch:
468
+ for item in response["Items"]:
469
+ await batch.delete_item(Key={"cache_key": item["cache_key"]})
470
+
471
+ # Handle pagination
472
+ while "LastEvaluatedKey" in response:
473
+ response = await table.scan(
474
+ FilterExpression="begins_with(cache_key, :prefix)",
475
+ ExpressionAttributeValues={":prefix": f"{self._namespace}:"},
476
+ ProjectionExpression="cache_key",
477
+ ExclusiveStartKey=response["LastEvaluatedKey"],
478
+ )
479
+
480
+ if response.get("Items"):
481
+ async with table.batch_writer() as batch:
482
+ for item in response["Items"]:
483
+ await batch.delete_item(Key={"cache_key": item["cache_key"]})
484
+
485
+ except Exception:
486
+ pass
487
+
488
+ async def close(self) -> None:
489
+ """
490
+ Close DynamoDB connections and clean up resources.
491
+ """
492
+ if self._async_resource:
493
+ await self._async_resource.__aexit__(None, None, None)
494
+ self._async_resource = None
495
+ self._async_table = None
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+ import inspect
2
3
  import pickle
3
4
  import re
4
5
  from datetime import datetime, timezone, timedelta
@@ -14,21 +15,17 @@ def _validate_namespace(namespace: str) -> str:
14
15
 
15
16
 
16
17
  def ensure_cleanup_task(method):
17
- @wraps(method)
18
- def sync_wrapper(self, *args, **kwargs):
19
- self._ensure_cleanup_task()
20
- return method(self, *args, **kwargs)
21
-
22
- @wraps(method)
23
- async def async_wrapper(self, *args, **kwargs):
24
- self._ensure_cleanup_task()
25
- return await method(self, *args, **kwargs)
26
-
27
- import inspect
28
18
  if inspect.iscoroutinefunction(method):
29
- return async_wrapper
19
+ @wraps(method)
20
+ async def wrapper(self, *args, **kwargs):
21
+ self._ensure_cleanup_task()
22
+ return await method(self, *args, **kwargs)
30
23
  else:
31
- return sync_wrapper
24
+ @wraps(method)
25
+ def wrapper(self, *args, **kwargs):
26
+ self._ensure_cleanup_task()
27
+ return method(self, *args, **kwargs)
28
+ return wrapper
32
29
 
33
30
  class PostgresBackend(CacheBackend):
34
31
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-cachekit
3
- Version: 0.1.3
3
+ Version: 0.1.4
4
4
  Summary: High-performance caching solution for FastAPI applications
5
5
  Author-email: Bijay Nayak <bijay6779@gmail.com>
6
6
  License-Expression: MIT
@@ -39,6 +39,9 @@ Provides-Extra: mongodb
39
39
  Requires-Dist: pymongo[gssapi,snappy,srv]>=4.6.0; extra == "mongodb"
40
40
  Provides-Extra: firestore
41
41
  Requires-Dist: google-cloud-firestore>=2.3.0; extra == "firestore"
42
+ Provides-Extra: dynamodb
43
+ Requires-Dist: boto3>=1.10.0; extra == "dynamodb"
44
+ Requires-Dist: aioboto3>=6.0.0; extra == "dynamodb"
42
45
  Provides-Extra: all
43
46
  Requires-Dist: redis>=4.2.0; extra == "all"
44
47
  Requires-Dist: psycopg[pool]>=3.2.9; extra == "all"
@@ -46,6 +49,8 @@ Requires-Dist: aiomcache>=0.8.1; extra == "all"
46
49
  Requires-Dist: pymemcache>=4.0.0; extra == "all"
47
50
  Requires-Dist: pymongo[gssapi,snappy,srv]>=4.6.0; extra == "all"
48
51
  Requires-Dist: google-cloud-firestore>=2.3.0; extra == "all"
52
+ Requires-Dist: boto3>=1.10.0; extra == "all"
53
+ Requires-Dist: aioboto3>=6.0.0; extra == "all"
49
54
  Dynamic: license-file
50
55
 
51
56
  # fastapi-cachekit
@@ -78,6 +83,7 @@ A high-performance, flexible caching solution for FastAPI applications. fastapi-
78
83
  | `MemcachedBackend` | ✅ | ✅ | `memcached` |
79
84
  | `MongoDB` | ✅ | ✅ | `mongodb` |
80
85
  | `FireStore` | ✅ | ✅ | `firestore` |
86
+ | `DynamoDBBackend` | ✅ | ✅ | `dynamodb` |
81
87
 
82
88
  ---
83
89
 
@@ -112,6 +118,11 @@ pip install fastapi-cachekit[mongodb]
112
118
  pip install fastapi-cachekit[firestore]
113
119
  ```
114
120
 
121
+ **With DynamoDB:**
122
+ ```bash
123
+ pip install fastapi-cachekit[dynamodb]
124
+ ```
125
+
115
126
  **All backends:**
116
127
  ```bash
117
128
  pip install fastapi-cachekit[all]
@@ -5,6 +5,7 @@ fast_cache/__init__.py
5
5
  fast_cache/integration.py
6
6
  fast_cache/backends/__init__.py
7
7
  fast_cache/backends/backend.py
8
+ fast_cache/backends/dynamodb.py
8
9
  fast_cache/backends/google_firestore.py
9
10
  fast_cache/backends/memcached.py
10
11
  fast_cache/backends/memory.py
@@ -7,6 +7,12 @@ aiomcache>=0.8.1
7
7
  pymemcache>=4.0.0
8
8
  pymongo[gssapi,snappy,srv]>=4.6.0
9
9
  google-cloud-firestore>=2.3.0
10
+ boto3>=1.10.0
11
+ aioboto3>=6.0.0
12
+
13
+ [dynamodb]
14
+ boto3>=1.10.0
15
+ aioboto3>=6.0.0
10
16
 
11
17
  [firestore]
12
18
  google-cloud-firestore>=2.3.0
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fastapi-cachekit"
3
- version = "0.1.3"
3
+ version = "0.1.4"
4
4
  description = "High-performance caching solution for FastAPI applications"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -69,13 +69,19 @@ mongodb = [
69
69
  firestore = [
70
70
  'google-cloud-firestore>=2.3.0'
71
71
  ]
72
+ dynamodb = [
73
+ 'boto3>=1.10.0',
74
+ 'aioboto3>=6.0.0'
75
+ ]
72
76
  all = [
73
77
  "redis>=4.2.0",
74
78
  "psycopg[pool]>=3.2.9",
75
79
  "aiomcache>=0.8.1",
76
80
  "pymemcache>=4.0.0",
77
81
  "pymongo[snappy,gssapi,srv]>=4.6.0",
78
- 'google-cloud-firestore>=2.3.0'
82
+ 'google-cloud-firestore>=2.3.0',
83
+ 'boto3>=1.10.0',
84
+ 'aioboto3>=6.0.0'
79
85
  ]
80
86
 
81
87
  [dependency-groups]