fastapi-cachekit 0.1.0__tar.gz → 0.1.2__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 (19) hide show
  1. fastapi_cachekit-0.1.2/LICENSE.md +21 -0
  2. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/PKG-INFO +8 -4
  3. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/README.md +1 -1
  4. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fast_cache/__init__.py +2 -1
  5. fastapi_cachekit-0.1.2/fast_cache/backends/mongodb.py +220 -0
  6. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fast_cache/backends/postgres.py +14 -1
  7. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fastapi_cachekit.egg-info/PKG-INFO +8 -4
  8. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fastapi_cachekit.egg-info/SOURCES.txt +2 -0
  9. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fastapi_cachekit.egg-info/requires.txt +4 -1
  10. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/pyproject.toml +10 -5
  11. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fast_cache/backends/__init__.py +0 -0
  12. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fast_cache/backends/backend.py +0 -0
  13. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fast_cache/backends/memcached.py +0 -0
  14. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fast_cache/backends/memory.py +0 -0
  15. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fast_cache/backends/redis.py +0 -0
  16. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fast_cache/integration.py +0 -0
  17. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fastapi_cachekit.egg-info/dependency_links.txt +0 -0
  18. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/fastapi_cachekit.egg-info/top_level.txt +0 -0
  19. {fastapi_cachekit-0.1.0 → fastapi_cachekit-0.1.2}/setup.cfg +0 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Bijay Nayak
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-cachekit
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: High-performance caching solution for FastAPI applications
5
5
  Author-email: Bijay Nayak <bijay6779@gmail.com>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/devbijay/fast-cache
8
- Project-URL: Documentation, https://github.com/devbijay/fast-cache#readme
8
+ Project-URL: Documentation, https://devbijay.github.io/Fast-Cache/
9
9
  Project-URL: Repository, https://github.com/devbijay/fast-cache.git
10
10
  Project-URL: Issues, https://github.com/devbijay/fast-cache/issues
11
11
  Keywords: fastapi,cache,redis,async,python,starlette,asyncio
@@ -26,8 +26,8 @@ Classifier: Topic :: Utilities
26
26
  Classifier: Typing :: Typed
27
27
  Requires-Python: >=3.9
28
28
  Description-Content-Type: text/markdown
29
+ License-File: LICENSE.md
29
30
  Requires-Dist: fastapi>=0.75.0
30
- Requires-Dist: uvicorn[standard]>=0.20.0
31
31
  Provides-Extra: redis
32
32
  Requires-Dist: redis>=4.2.0; extra == "redis"
33
33
  Provides-Extra: postgres
@@ -35,11 +35,15 @@ Requires-Dist: psycopg[pool]>=3.2.9; extra == "postgres"
35
35
  Provides-Extra: memcached
36
36
  Requires-Dist: aiomcache>=0.8.1; extra == "memcached"
37
37
  Requires-Dist: pymemcache>=4.0.0; extra == "memcached"
38
+ Provides-Extra: mongodb
39
+ Requires-Dist: pymongo[gssapi,snappy,srv]>=4.6.0; extra == "mongodb"
38
40
  Provides-Extra: all
39
41
  Requires-Dist: redis>=4.2.0; extra == "all"
40
42
  Requires-Dist: psycopg[pool]>=3.2.9; extra == "all"
41
43
  Requires-Dist: aiomcache>=0.8.1; extra == "all"
42
44
  Requires-Dist: pymemcache>=4.0.0; extra == "all"
45
+ Requires-Dist: pymongo[gssapi,snappy,srv]>=4.6.0; extra == "all"
46
+ Dynamic: license-file
43
47
 
44
48
  # fastapi-cachekit
45
49
 
@@ -47,7 +51,7 @@ A high-performance, flexible caching solution for FastAPI applications. fastapi-
47
51
 
48
52
  [![PyPI version](https://badge.fury.io/py/fastapi-cachekit.svg)](https://badge.fury.io/py/fastapi-cachekit)
49
53
  [![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)
54
+ [![PyPI Downloads](https://static.pepy.tech/badge/fastapi-cachekit)](https://pepy.tech/projects/fastapi-cachekit)
51
55
 
52
56
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
53
57
 
@@ -4,7 +4,7 @@ A high-performance, flexible caching solution for FastAPI applications. fastapi-
4
4
 
5
5
  [![PyPI version](https://badge.fury.io/py/fastapi-cachekit.svg)](https://badge.fury.io/py/fastapi-cachekit)
6
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)
7
+ [![PyPI Downloads](https://static.pepy.tech/badge/fastapi-cachekit)](https://pepy.tech/projects/fastapi-cachekit)
8
8
 
9
9
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
10
10
 
@@ -5,8 +5,9 @@ from .backends.redis import RedisBackend
5
5
  from .backends.memory import InMemoryBackend
6
6
  from .backends.postgres import PostgresBackend
7
7
  from .backends.memcached import MemcachedBackend
8
+ from .backends.mongodb import MongoDBBackend
8
9
 
9
- __all__ = ["FastAPICache", "RedisBackend", "CacheBackend", "InMemoryBackend","PostgresBackend", "cache","MemcachedBackend" ]
10
+ __all__ = ["FastAPICache", "RedisBackend", "CacheBackend", "InMemoryBackend","PostgresBackend", "cache","MemcachedBackend", "MongoDBBackend" ]
10
11
 
11
12
 
12
13
  # Create global cache instance
@@ -0,0 +1,220 @@
1
+ import pickle
2
+ import time
3
+ from typing import Any, Optional, Union
4
+ from datetime import timedelta
5
+ from .backend import CacheBackend
6
+
7
+ class MongoDBBackend(CacheBackend):
8
+ """
9
+ MongoDB cache backend with both sync and async support.
10
+ Uses a TTL index for automatic expiration of cache entries.
11
+
12
+ Each cache entry is stored as a document with:
13
+ - _id: the cache key (optionally namespaced)
14
+ - value: the pickled cached value
15
+ - expires_at: epoch time when the entry should expire
16
+
17
+ Expired documents are deleted automatically by MongoDB's TTL monitor,
18
+ but expiration is also checked in code to avoid returning stale data.
19
+ """
20
+
21
+ def __init__(
22
+ self,
23
+ uri: str,
24
+ namespace: Optional[str] = "fastapi_cache"
25
+ ) -> None:
26
+ """
27
+ Initialize the MongoDB backend.
28
+
29
+ Args:
30
+ uri (str): MongoDB connection URI (should include the database name).
31
+ namespace (Optional[str]): Optional prefix for all cache keys and the collection name.
32
+ Defaults to "fastapi_cache".
33
+ Raises:
34
+ ImportError: If pymongo is not installed.
35
+ """
36
+ try:
37
+ import pymongo
38
+ except ImportError:
39
+ raise ImportError(
40
+ "MongoDBBackend requires 'pymongo>=4.6.0'. "
41
+ "Install with: pip install fastapi-cachekit[mongodb]"
42
+ )
43
+ self._namespace = namespace or "cache"
44
+
45
+ self._sync_client = pymongo.MongoClient(uri)
46
+ self._sync_db = self._sync_client.get_default_database()
47
+ self._sync_collection = self._sync_db[self._namespace]
48
+ self._sync_collection.create_index("expires_at", expireAfterSeconds=0)
49
+
50
+ # Async client
51
+ self._async_client = pymongo.AsyncMongoClient(uri)
52
+ self._async_db = self._async_client.get_default_database()
53
+ self._async_collection = self._async_db[self._namespace]
54
+
55
+ def _make_key(self, key: str) -> str:
56
+ """
57
+ Create a namespaced cache key.
58
+
59
+ Args:
60
+ key (str): The original cache key.
61
+
62
+ Returns:
63
+ str: The namespaced cache key.
64
+ """
65
+ return f"{self._namespace}:{key}"
66
+
67
+ def get(self, key: str) -> Optional[Any]:
68
+ """
69
+ Synchronously retrieve a value from the cache.
70
+
71
+ Args:
72
+ key (str): The cache key.
73
+
74
+ Returns:
75
+ Optional[Any]: The cached value, or None if not found or expired.
76
+ """
77
+ doc = self._sync_collection.find_one({"_id": self._make_key(key)})
78
+ if doc and (doc.get("expires_at", float("inf")) > time.time()):
79
+ return pickle.loads(doc["value"])
80
+ return None
81
+
82
+ def set(
83
+ self,
84
+ key: str,
85
+ value: Any,
86
+ expire: Optional[Union[int, timedelta]] = None
87
+ ) -> None:
88
+ """
89
+ Synchronously set a value in the cache.
90
+
91
+ Args:
92
+ key (str): The cache key.
93
+ value (Any): The value to cache.
94
+ expire (Optional[Union[int, timedelta]]): Expiration time in seconds or as timedelta.
95
+ If None, the entry never expires.
96
+ """
97
+ update = {"value": pickle.dumps(value)}
98
+ if expire is not None:
99
+ if isinstance(expire, timedelta):
100
+ exptime = int(time.time() + expire.total_seconds())
101
+ else:
102
+ exptime = int(time.time() + expire)
103
+ update["expires_at"] = exptime
104
+
105
+ self._sync_collection.update_one(
106
+ {"_id": self._make_key(key)}, {"$set": update}, upsert=True
107
+ )
108
+
109
+ def delete(self, key: str) -> None:
110
+ """
111
+ Synchronously delete a value from the cache.
112
+
113
+ Args:
114
+ key (str): The cache key.
115
+ """
116
+ self._sync_collection.delete_one({"_id": self._make_key(key)})
117
+
118
+ def clear(self) -> None:
119
+ """
120
+ Synchronously clear all values from the namespace.
121
+ """
122
+ self._sync_collection.delete_many({"_id": {"$regex": f"^{self._namespace}:"}})
123
+
124
+ def has(self, key: str) -> bool:
125
+ """
126
+ Synchronously check if a key exists in the cache.
127
+
128
+ Args:
129
+ key (str): The cache key.
130
+
131
+ Returns:
132
+ bool: True if the key exists and is not expired, False otherwise.
133
+ """
134
+ doc = self._sync_collection.find_one({"_id": self._make_key(key)})
135
+ return bool(doc and (doc.get("expires_at", float("inf")) > time.time()))
136
+
137
+ async def aget(self, key: str) -> Optional[Any]:
138
+ """
139
+ Asynchronously retrieve a value from the cache.
140
+
141
+ Args:
142
+ key (str): The cache key.
143
+
144
+ Returns:
145
+ Optional[Any]: The cached value, or None if not found or expired.
146
+ """
147
+ doc = await self._async_collection.find_one({"_id": self._make_key(key)})
148
+ if doc and (doc.get("expires_at", float("inf")) > time.time()):
149
+ return pickle.loads(doc["value"])
150
+ return None
151
+
152
+ async def aset(
153
+ self,
154
+ key: str,
155
+ value: Any,
156
+ expire: Optional[Union[int, timedelta]] = None
157
+ ) -> None:
158
+ """
159
+ Asynchronously set a value in the cache.
160
+
161
+ Args:
162
+ key (str): The cache key.
163
+ value (Any): The value to cache.
164
+ expire (Optional[Union[int, timedelta]]): Expiration time in seconds or as timedelta.
165
+ If None, the entry never expires.
166
+ """
167
+ update = {"value": pickle.dumps(value)}
168
+ if expire is not None:
169
+ if isinstance(expire, timedelta):
170
+ exptime = int(time.time() + expire.total_seconds())
171
+ else:
172
+ exptime = int(time.time() + expire)
173
+ update["expires_at"] = exptime
174
+
175
+ await self._async_collection.update_one(
176
+ {"_id": self._make_key(key)},
177
+ {"$set": update},
178
+ upsert=True
179
+ )
180
+
181
+ async def adelete(self, key: str) -> None:
182
+ """
183
+ Asynchronously delete a value from the cache.
184
+
185
+ Args:
186
+ key (str): The cache key.
187
+ """
188
+ await self._async_collection.delete_one({"_id": self._make_key(key)})
189
+
190
+ async def aclear(self) -> None:
191
+ """
192
+ Asynchronously clear all values from the namespace.
193
+ """
194
+ await self._async_collection.delete_many({"_id": {"$regex": f"^{self._namespace}:"}})
195
+
196
+ async def ahas(self, key: str) -> bool:
197
+ """
198
+ Asynchronously check if a key exists in the cache.
199
+
200
+ Args:
201
+ key (str): The cache key.
202
+
203
+ Returns:
204
+ bool: True if the key exists and is not expired, False otherwise.
205
+ """
206
+ doc = await self._async_collection.find_one({"_id": self._make_key(key)})
207
+ return bool(doc and (doc.get("expires_at", float("inf")) > time.time()))
208
+
209
+ def close(self) -> None:
210
+ """
211
+ Close the synchronous MongoDB client.
212
+ """
213
+ self._sync_client.close()
214
+
215
+ async def aclose(self) -> None:
216
+ """
217
+ Close the asynchronous MongoDB client.
218
+ """
219
+ self._sync_client.close()
220
+ await self._async_client.close()
@@ -1,9 +1,15 @@
1
1
  import pickle
2
+ import re
2
3
  from datetime import datetime, timezone, timedelta
3
4
  from typing import Any, Optional, Union
4
5
  from .backend import CacheBackend
5
6
 
6
7
 
8
+ def _validate_namespace(namespace: str) -> str:
9
+ if not re.match(r"^[A-Za-z0-9_]+$", namespace):
10
+ raise ValueError("Invalid namespace: only alphanumeric and underscore allowed")
11
+ return namespace
12
+
7
13
  class PostgresBackend(CacheBackend):
8
14
  """
9
15
  PostgreSQL cache backend implementation.
@@ -26,7 +32,7 @@ class PostgresBackend(CacheBackend):
26
32
  "Install it with: pip install fast-cache[postgres]"
27
33
  )
28
34
 
29
- self._namespace = namespace
35
+ self._namespace = _validate_namespace(namespace)
30
36
  self._table_name = f"{namespace}_cache_store"
31
37
 
32
38
  # The pools are opened on creation and will auto-reopen if needed
@@ -39,6 +45,13 @@ class PostgresBackend(CacheBackend):
39
45
  )
40
46
  self._create_unlogged_table_if_not_exists()
41
47
 
48
+ def _validate_namespace(namespace: str) -> str:
49
+ if not re.match(r"^[A-Za-z0-9_]+$", namespace):
50
+ raise ValueError(
51
+ "Invalid namespace: only alphanumeric and underscore allowed"
52
+ )
53
+ return namespace
54
+
42
55
  def _create_unlogged_table_if_not_exists(self):
43
56
  """Create the cache table if it doesn't exist."""
44
57
  # The index on expire_at is for efficient periodic cleanup jobs,
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastapi-cachekit
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: High-performance caching solution for FastAPI applications
5
5
  Author-email: Bijay Nayak <bijay6779@gmail.com>
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/devbijay/fast-cache
8
- Project-URL: Documentation, https://github.com/devbijay/fast-cache#readme
8
+ Project-URL: Documentation, https://devbijay.github.io/Fast-Cache/
9
9
  Project-URL: Repository, https://github.com/devbijay/fast-cache.git
10
10
  Project-URL: Issues, https://github.com/devbijay/fast-cache/issues
11
11
  Keywords: fastapi,cache,redis,async,python,starlette,asyncio
@@ -26,8 +26,8 @@ Classifier: Topic :: Utilities
26
26
  Classifier: Typing :: Typed
27
27
  Requires-Python: >=3.9
28
28
  Description-Content-Type: text/markdown
29
+ License-File: LICENSE.md
29
30
  Requires-Dist: fastapi>=0.75.0
30
- Requires-Dist: uvicorn[standard]>=0.20.0
31
31
  Provides-Extra: redis
32
32
  Requires-Dist: redis>=4.2.0; extra == "redis"
33
33
  Provides-Extra: postgres
@@ -35,11 +35,15 @@ Requires-Dist: psycopg[pool]>=3.2.9; extra == "postgres"
35
35
  Provides-Extra: memcached
36
36
  Requires-Dist: aiomcache>=0.8.1; extra == "memcached"
37
37
  Requires-Dist: pymemcache>=4.0.0; extra == "memcached"
38
+ Provides-Extra: mongodb
39
+ Requires-Dist: pymongo[gssapi,snappy,srv]>=4.6.0; extra == "mongodb"
38
40
  Provides-Extra: all
39
41
  Requires-Dist: redis>=4.2.0; extra == "all"
40
42
  Requires-Dist: psycopg[pool]>=3.2.9; extra == "all"
41
43
  Requires-Dist: aiomcache>=0.8.1; extra == "all"
42
44
  Requires-Dist: pymemcache>=4.0.0; extra == "all"
45
+ Requires-Dist: pymongo[gssapi,snappy,srv]>=4.6.0; extra == "all"
46
+ Dynamic: license-file
43
47
 
44
48
  # fastapi-cachekit
45
49
 
@@ -47,7 +51,7 @@ A high-performance, flexible caching solution for FastAPI applications. fastapi-
47
51
 
48
52
  [![PyPI version](https://badge.fury.io/py/fastapi-cachekit.svg)](https://badge.fury.io/py/fastapi-cachekit)
49
53
  [![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)
54
+ [![PyPI Downloads](https://static.pepy.tech/badge/fastapi-cachekit)](https://pepy.tech/projects/fastapi-cachekit)
51
55
 
52
56
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
53
57
 
@@ -1,3 +1,4 @@
1
+ LICENSE.md
1
2
  README.md
2
3
  pyproject.toml
3
4
  fast_cache/__init__.py
@@ -6,6 +7,7 @@ fast_cache/backends/__init__.py
6
7
  fast_cache/backends/backend.py
7
8
  fast_cache/backends/memcached.py
8
9
  fast_cache/backends/memory.py
10
+ fast_cache/backends/mongodb.py
9
11
  fast_cache/backends/postgres.py
10
12
  fast_cache/backends/redis.py
11
13
  fastapi_cachekit.egg-info/PKG-INFO
@@ -1,16 +1,19 @@
1
1
  fastapi>=0.75.0
2
- uvicorn[standard]>=0.20.0
3
2
 
4
3
  [all]
5
4
  redis>=4.2.0
6
5
  psycopg[pool]>=3.2.9
7
6
  aiomcache>=0.8.1
8
7
  pymemcache>=4.0.0
8
+ pymongo[gssapi,snappy,srv]>=4.6.0
9
9
 
10
10
  [memcached]
11
11
  aiomcache>=0.8.1
12
12
  pymemcache>=4.0.0
13
13
 
14
+ [mongodb]
15
+ pymongo[gssapi,snappy,srv]>=4.6.0
16
+
14
17
  [postgres]
15
18
  psycopg[pool]>=3.2.9
16
19
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fastapi-cachekit"
3
- version = "0.1.0"
3
+ version = "0.1.2"
4
4
  description = "High-performance caching solution for FastAPI applications"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -28,13 +28,12 @@ classifiers = [
28
28
  ]
29
29
 
30
30
  dependencies = [
31
- "fastapi>=0.75.0",
32
- "uvicorn[standard]>=0.20.0",
31
+ "fastapi>=0.75.0"
33
32
  ]
34
33
 
35
34
  [project.urls]
36
35
  Homepage = "https://github.com/devbijay/fast-cache"
37
- Documentation = "https://github.com/devbijay/fast-cache#readme"
36
+ Documentation = "https://devbijay.github.io/Fast-Cache/"
38
37
  Repository = "https://github.com/devbijay/fast-cache.git"
39
38
  Issues = "https://github.com/devbijay/fast-cache/issues"
40
39
 
@@ -64,16 +63,22 @@ memcached = [
64
63
  "aiomcache>=0.8.1",
65
64
  "pymemcache>=4.0.0"
66
65
  ]
66
+ mongodb = [
67
+ "pymongo[snappy,gssapi,srv]>=4.6.0"
68
+ ]
67
69
  all = [
68
70
  "redis>=4.2.0",
69
71
  "psycopg[pool]>=3.2.9",
70
72
  "aiomcache>=0.8.1",
71
- "pymemcache>=4.0.0"
73
+ "pymemcache>=4.0.0",
74
+ "pymongo[snappy,gssapi,srv]>=4.6.0"
72
75
  ]
73
76
 
74
77
  [dependency-groups]
75
78
  dev = [
76
79
  "httpx>=0.28.1",
80
+ "mkdocs-material>=9.6.14",
81
+ "mkdocstrings[python]>=0.29.1",
77
82
  "psycopg-binary>=3.2.9",
78
83
  "pytest>=8.4.0",
79
84
  "pytest-asyncio>=1.0.0",