mongospec 0.2__tar.gz → 0.2.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 (41) hide show
  1. {mongospec-0.2/mongospec.egg-info → mongospec-0.2.2}/PKG-INFO +63 -1
  2. {mongospec-0.2 → mongospec-0.2.2}/README.md +62 -0
  3. mongospec-0.2.2/mongospec/contrib/__init__.py +0 -0
  4. mongospec-0.2.2/mongospec/contrib/kv_store.py +280 -0
  5. {mongospec-0.2 → mongospec-0.2.2}/mongospec/document/document.py +17 -1
  6. {mongospec-0.2 → mongospec-0.2.2/mongospec.egg-info}/PKG-INFO +63 -1
  7. {mongospec-0.2 → mongospec-0.2.2}/mongospec.egg-info/SOURCES.txt +2 -0
  8. {mongospec-0.2 → mongospec-0.2.2}/pyproject.toml +1 -1
  9. {mongospec-0.2 → mongospec-0.2.2}/LICENSE +0 -0
  10. {mongospec-0.2 → mongospec-0.2.2}/examples/atomic_updates.py +0 -0
  11. {mongospec-0.2 → mongospec-0.2.2}/examples/batch_operations.py +0 -0
  12. {mongospec-0.2 → mongospec-0.2.2}/examples/collection_binding.py +0 -0
  13. {mongospec-0.2 → mongospec-0.2.2}/examples/connection_management.py +0 -0
  14. {mongospec-0.2 → mongospec-0.2.2}/examples/count_documents.py +0 -0
  15. {mongospec-0.2 → mongospec-0.2.2}/examples/create_documents.py +0 -0
  16. {mongospec-0.2 → mongospec-0.2.2}/examples/delete_documents.py +0 -0
  17. {mongospec-0.2 → mongospec-0.2.2}/examples/document_models.py +0 -0
  18. {mongospec-0.2 → mongospec-0.2.2}/examples/index_creation.py +0 -0
  19. {mongospec-0.2 → mongospec-0.2.2}/examples/quick_start.py +0 -0
  20. {mongospec-0.2 → mongospec-0.2.2}/examples/read_documents.py +0 -0
  21. {mongospec-0.2 → mongospec-0.2.2}/examples/update_documents.py +0 -0
  22. {mongospec-0.2 → mongospec-0.2.2}/examples/upsert_operations.py +0 -0
  23. {mongospec-0.2 → mongospec-0.2.2}/examples/working_with_cursors.py +0 -0
  24. {mongospec-0.2 → mongospec-0.2.2}/mongospec/__init__.py +0 -0
  25. {mongospec-0.2 → mongospec-0.2.2}/mongospec/_connection.py +0 -0
  26. {mongospec-0.2 → mongospec-0.2.2}/mongospec/core.py +0 -0
  27. {mongospec-0.2 → mongospec-0.2.2}/mongospec/document/__init__.py +0 -0
  28. {mongospec-0.2 → mongospec-0.2.2}/mongospec/document/operations/__init__.py +0 -0
  29. {mongospec-0.2 → mongospec-0.2.2}/mongospec/document/operations/base.py +0 -0
  30. {mongospec-0.2 → mongospec-0.2.2}/mongospec/document/operations/count.py +0 -0
  31. {mongospec-0.2 → mongospec-0.2.2}/mongospec/document/operations/delete.py +0 -0
  32. {mongospec-0.2 → mongospec-0.2.2}/mongospec/document/operations/find.py +0 -0
  33. {mongospec-0.2 → mongospec-0.2.2}/mongospec/document/operations/insert.py +0 -0
  34. {mongospec-0.2 → mongospec-0.2.2}/mongospec/document/operations/update.py +0 -0
  35. {mongospec-0.2 → mongospec-0.2.2}/mongospec/utils.py +0 -0
  36. {mongospec-0.2 → mongospec-0.2.2}/mongospec.egg-info/dependency_links.txt +0 -0
  37. {mongospec-0.2 → mongospec-0.2.2}/mongospec.egg-info/requires.txt +0 -0
  38. {mongospec-0.2 → mongospec-0.2.2}/mongospec.egg-info/top_level.txt +0 -0
  39. {mongospec-0.2 → mongospec-0.2.2}/setup.cfg +0 -0
  40. {mongospec-0.2 → mongospec-0.2.2}/tests/conftest.py +0 -0
  41. {mongospec-0.2 → mongospec-0.2.2}/tests/test_connection.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mongospec
3
- Version: 0.2
3
+ Version: 0.2.2
4
4
  Summary: Async MongoDB ODM with msgspec integration and automatic collection binding
5
5
  Author-email: Diprog <diprog991@gmail.com>
6
6
  License-Expression: MIT
@@ -51,6 +51,7 @@ Minimal **async** MongoDB ODM built for *speed* and *simplicity*, featuring auto
51
51
  - [CRUD Operations](#crud-operations)
52
52
  - [Indexes](#indexes)
53
53
  - [Lifecycle Hooks](#lifecycle-hooks)
54
+ - [Contrib: KV Store](#contrib-kv-store)
54
55
  6. [Contributing](#contributing)
55
56
  7. [License](#license)
56
57
 
@@ -271,6 +272,67 @@ in sync — no caller-side boilerplate needed.
271
272
 
272
273
  ---
273
274
 
275
+ ### Contrib: KV Store
276
+
277
+ `mongospec.contrib.kv_store` provides a ready-made async key-value store
278
+ backed by a MongoDB collection. Designed for multiple inheritance with
279
+ project-specific base documents.
280
+
281
+ ```python
282
+ from mongospec.contrib.kv_store import KVStore, KVStoreItem
283
+ from myapp.db import Document # your base with timestamps, hooks, etc.
284
+
285
+
286
+ class AppStorage(KVStore, Document):
287
+ __collection_name__ = "app_storage"
288
+ ```
289
+
290
+ A unique index on `key` is created automatically at init time.
291
+
292
+ **Direct usage:**
293
+
294
+ ```python
295
+ await AppStorage.set("theme", "dark")
296
+ theme = await AppStorage.get("theme") # "dark"
297
+ theme = await AppStorage.get_or_default("x", 0) # 0 (no KeyError)
298
+ await AppStorage.set_default("theme", "light") # "dark" (atomic, no overwrite)
299
+
300
+ await AppStorage.set_many({"a": 1, "b": 2})
301
+ all_pairs = await AppStorage.get_all() # {"theme": "dark", "a": 1, "b": 2}
302
+ all_keys = await AppStorage.keys() # ["theme", "a", "b"]
303
+
304
+ await AppStorage.has("theme") # True
305
+ await AppStorage.delete_key("theme") # True
306
+ ```
307
+
308
+ **Typed accessor (`KVStoreItem`):**
309
+
310
+ ```python
311
+ AppStorageItem = KVStoreItem.of(AppStorage)
312
+
313
+ max_retries = AppStorageItem[int]("max_retries", default=3)
314
+
315
+ value = await max_retries.get() # 3 (default, not persisted)
316
+ await max_retries.set_default() # atomically persist default if missing
317
+ await max_retries.set(10)
318
+ await max_retries.has() # True
319
+ await max_retries.delete() # True
320
+ ```
321
+
322
+ | `KVStore` method | Description |
323
+ |------------------|-------------|
324
+ | `set(key, value)` | Upsert a value by key |
325
+ | `get(key)` | Get value or raise `KeyError` |
326
+ | `get_or_default(key, default)` | Get value or return default |
327
+ | `set_default(key, value)` | Atomic insert-if-absent (`$setOnInsert`) |
328
+ | `delete_key(key)` | Delete a key, return `True` if existed |
329
+ | `has(key)` | Check key existence |
330
+ | `get_all()` | Return all pairs as `dict` |
331
+ | `keys()` | Return all key names |
332
+ | `set_many(items)` | Upsert multiple pairs |
333
+
334
+ ---
335
+
274
336
  ## Contributing
275
337
 
276
338
  Contributions, issues and feature requests are welcome.
@@ -24,6 +24,7 @@ Minimal **async** MongoDB ODM built for *speed* and *simplicity*, featuring auto
24
24
  - [CRUD Operations](#crud-operations)
25
25
  - [Indexes](#indexes)
26
26
  - [Lifecycle Hooks](#lifecycle-hooks)
27
+ - [Contrib: KV Store](#contrib-kv-store)
27
28
  6. [Contributing](#contributing)
28
29
  7. [License](#license)
29
30
 
@@ -244,6 +245,67 @@ in sync — no caller-side boilerplate needed.
244
245
 
245
246
  ---
246
247
 
248
+ ### Contrib: KV Store
249
+
250
+ `mongospec.contrib.kv_store` provides a ready-made async key-value store
251
+ backed by a MongoDB collection. Designed for multiple inheritance with
252
+ project-specific base documents.
253
+
254
+ ```python
255
+ from mongospec.contrib.kv_store import KVStore, KVStoreItem
256
+ from myapp.db import Document # your base with timestamps, hooks, etc.
257
+
258
+
259
+ class AppStorage(KVStore, Document):
260
+ __collection_name__ = "app_storage"
261
+ ```
262
+
263
+ A unique index on `key` is created automatically at init time.
264
+
265
+ **Direct usage:**
266
+
267
+ ```python
268
+ await AppStorage.set("theme", "dark")
269
+ theme = await AppStorage.get("theme") # "dark"
270
+ theme = await AppStorage.get_or_default("x", 0) # 0 (no KeyError)
271
+ await AppStorage.set_default("theme", "light") # "dark" (atomic, no overwrite)
272
+
273
+ await AppStorage.set_many({"a": 1, "b": 2})
274
+ all_pairs = await AppStorage.get_all() # {"theme": "dark", "a": 1, "b": 2}
275
+ all_keys = await AppStorage.keys() # ["theme", "a", "b"]
276
+
277
+ await AppStorage.has("theme") # True
278
+ await AppStorage.delete_key("theme") # True
279
+ ```
280
+
281
+ **Typed accessor (`KVStoreItem`):**
282
+
283
+ ```python
284
+ AppStorageItem = KVStoreItem.of(AppStorage)
285
+
286
+ max_retries = AppStorageItem[int]("max_retries", default=3)
287
+
288
+ value = await max_retries.get() # 3 (default, not persisted)
289
+ await max_retries.set_default() # atomically persist default if missing
290
+ await max_retries.set(10)
291
+ await max_retries.has() # True
292
+ await max_retries.delete() # True
293
+ ```
294
+
295
+ | `KVStore` method | Description |
296
+ |------------------|-------------|
297
+ | `set(key, value)` | Upsert a value by key |
298
+ | `get(key)` | Get value or raise `KeyError` |
299
+ | `get_or_default(key, default)` | Get value or return default |
300
+ | `set_default(key, value)` | Atomic insert-if-absent (`$setOnInsert`) |
301
+ | `delete_key(key)` | Delete a key, return `True` if existed |
302
+ | `has(key)` | Check key existence |
303
+ | `get_all()` | Return all pairs as `dict` |
304
+ | `keys()` | Return all key names |
305
+ | `set_many(items)` | Upsert multiple pairs |
306
+
307
+ ---
308
+
247
309
  ## Contributing
248
310
 
249
311
  Contributions, issues and feature requests are welcome.
File without changes
@@ -0,0 +1,280 @@
1
+ """
2
+ Key-value store built on top of MongoDocument.
3
+
4
+ Provides a simple async key-value interface over a MongoDB collection.
5
+ Designed for use via multiple inheritance with project-specific base documents.
6
+
7
+ .. code-block:: python
8
+
9
+ from mongospec.contrib.kv_store import KVStoreMixin, KVStoreItem
10
+
11
+ class AppStorage(KVStoreMixin, Document):
12
+ __collection_name__ = "app_storage"
13
+
14
+ max_retries = KVStoreItem[int](AppStorage, "max_retries", default=3)
15
+ value = await max_retries.get()
16
+ await max_retries.set(5)
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from collections.abc import Sequence
22
+ from typing import Any, ClassVar, Generic, TypeVar
23
+
24
+ import mongojet
25
+
26
+ from mongospec.document.document import MongoDocument
27
+
28
+ T = TypeVar("T")
29
+
30
+
31
+ class KVStoreMixin:
32
+ """
33
+ Mixin providing async key-value store methods.
34
+
35
+ Does **not** define struct fields — safe for multiple inheritance
36
+ with any ``MongoDocument`` subclass. The concrete class must define
37
+ ``key: str`` and ``value: Any | None`` fields.
38
+
39
+ .. code-block:: python
40
+
41
+ class AppStorage(KVStoreMixin, Document):
42
+ __collection_name__ = "app_storage"
43
+
44
+ await AppStorage.set("theme", "dark")
45
+ theme = await AppStorage.get("theme")
46
+ """
47
+
48
+ __indexes__: ClassVar[Sequence[mongojet.IndexModel]] = [
49
+ mongojet.IndexModel(keys=[("key", 1)], unique=True), # type: ignore[call-arg]
50
+ ]
51
+
52
+ @classmethod
53
+ async def set(cls, key: str, value: Any | None) -> None:
54
+ """
55
+ Upsert a value by key.
56
+
57
+ :param key: Unique setting key.
58
+ :param value: Value to store; may be ``None``.
59
+ """
60
+ await cls.update_one(
61
+ {"key": key},
62
+ {"$set": {"value": value}},
63
+ upsert=True,
64
+ )
65
+
66
+ @classmethod
67
+ async def get(cls, key: str) -> Any | None:
68
+ """
69
+ Retrieve a value by key.
70
+
71
+ :param key: Unique setting key.
72
+ :return: The stored value (may be ``None``).
73
+ :raises KeyError: If the key does not exist.
74
+ """
75
+ document = await cls.find_one({"key": key})
76
+ if document is None:
77
+ raise KeyError(key)
78
+ return document.value
79
+
80
+ @classmethod
81
+ async def get_or_default(cls, key: str, default: Any | None = None) -> Any | None:
82
+ """
83
+ Retrieve a value by key, returning *default* if missing.
84
+
85
+ Unlike :meth:`get`, this never raises :exc:`KeyError`.
86
+
87
+ :param key: Unique setting key.
88
+ :param default: Fallback value when the key is absent.
89
+ :return: The stored value or *default*.
90
+ """
91
+ document = await cls.find_one({"key": key})
92
+ if document is None:
93
+ return default
94
+ return document.value
95
+
96
+ @classmethod
97
+ async def set_default(cls, key: str, value: Any | None) -> Any | None:
98
+ """
99
+ Atomically set a value only if the key does not already exist.
100
+
101
+ :param key: Unique setting key.
102
+ :param value: Value to store when the key is missing.
103
+ :return: The existing value if present, otherwise *value*.
104
+ """
105
+ doc = await cls.find_one_and_update(
106
+ {"key": key},
107
+ {"$setOnInsert": {"key": key, "value": value}},
108
+ upsert=True,
109
+ return_updated=True,
110
+ )
111
+ return doc.value if doc is not None else value
112
+
113
+ @classmethod
114
+ async def delete_key(cls, key: str) -> bool:
115
+ """
116
+ Delete a key-value pair.
117
+
118
+ :param key: Unique setting key.
119
+ :return: ``True`` if the key existed and was deleted.
120
+ """
121
+ return await cls.delete_one({"key": key}) > 0
122
+
123
+ @classmethod
124
+ async def has(cls, key: str) -> bool:
125
+ """
126
+ Check whether a key exists.
127
+
128
+ :param key: Unique setting key.
129
+ :return: ``True`` if the key is present.
130
+ """
131
+ return await cls.exists({"key": key})
132
+
133
+ @classmethod
134
+ async def get_all(cls) -> dict[str, Any | None]:
135
+ """
136
+ Retrieve all key-value pairs as a dictionary.
137
+
138
+ :return: Mapping of all stored keys to their values.
139
+ """
140
+ docs = await cls.find_all({})
141
+ return {doc.key: doc.value for doc in docs}
142
+
143
+ @classmethod
144
+ async def keys(cls) -> list[str]:
145
+ """
146
+ Retrieve all stored keys.
147
+
148
+ :return: List of key names.
149
+ """
150
+ docs = await cls.find_all({})
151
+ return [doc.key for doc in docs]
152
+
153
+ @classmethod
154
+ async def set_many(cls, items: dict[str, Any | None]) -> None:
155
+ """
156
+ Upsert multiple key-value pairs at once.
157
+
158
+ :param items: Mapping of keys to values.
159
+ """
160
+ for key, value in items.items():
161
+ await cls.set(key, value)
162
+
163
+
164
+ class KVStore(KVStoreMixin, MongoDocument, kw_only=True):
165
+ """
166
+ Standalone async key-value store backed by a MongoDB collection.
167
+
168
+ Includes ``key`` and ``value`` fields. Use this directly if you
169
+ don't need to combine with a custom base document.
170
+ For multiple inheritance with your own ``Document`` base, use
171
+ :class:`KVStoreMixin` instead.
172
+
173
+ .. code-block:: python
174
+
175
+ class AppStorage(KVStore):
176
+ __collection_name__ = "app_storage"
177
+ """
178
+
179
+ key: str
180
+ value: Any | None = None
181
+
182
+
183
+ class KVStoreItem(Generic[T]):
184
+ """
185
+ Typed accessor for a single key in a :class:`KVStoreMixin` collection.
186
+
187
+ .. code-block:: python
188
+
189
+ max_retries = KVStoreItem[int](AppStorage, "max_retries", default=3)
190
+
191
+ value = await max_retries.get() # int | None
192
+ await max_retries.set(10)
193
+
194
+ :param store: The ``KVStoreMixin`` subclass to use.
195
+ :param key: Unique setting key.
196
+ :param default: Default value returned (and persisted) when the key is missing.
197
+ """
198
+
199
+ def __init__(
200
+ self,
201
+ store: type[KVStoreMixin],
202
+ key: str,
203
+ default: T | None = None,
204
+ ) -> None:
205
+ self._store = store
206
+ self._key = key
207
+ self._default = default
208
+
209
+ @classmethod
210
+ def of(cls, store: type[KVStoreMixin]) -> type[KVStoreItem]:
211
+ """
212
+ Create a ``KVStoreItem`` subclass bound to a specific store.
213
+
214
+ .. code-block:: python
215
+
216
+ AppStorageItem = KVStoreItem.of(AppStorage)
217
+ max_retries = AppStorageItem[int]("max_retries", default=3)
218
+
219
+ :param store: The ``KVStoreMixin`` subclass to bind.
220
+ :return: A new ``KVStoreItem`` subclass with *store* pre-filled.
221
+ """
222
+ bound_store = store
223
+
224
+ class BoundKVStoreItem(cls): # type: ignore[misc]
225
+ def __init__(
226
+ self,
227
+ key: str,
228
+ default: T | None = None,
229
+ ) -> None:
230
+ super().__init__(store=bound_store, key=key, default=default)
231
+
232
+ def __class_getitem__(cls, item: Any) -> type:
233
+ return cls
234
+
235
+ BoundKVStoreItem.__name__ = f"{store.__name__}Item"
236
+ BoundKVStoreItem.__qualname__ = f"{store.__name__}Item"
237
+ return BoundKVStoreItem
238
+
239
+ async def get(self) -> T | None:
240
+ """
241
+ Get the stored value, returning *default* if missing.
242
+
243
+ Does **not** persist the default — use :meth:`set_default`
244
+ to atomically initialize a key.
245
+
246
+ :return: The stored value cast to ``T``, or the default.
247
+ """
248
+ return await self._store.get_or_default(self._key, self._default) # type: ignore[return-value]
249
+
250
+ async def set(self, value: T | None) -> None:
251
+ """
252
+ Set the stored value.
253
+
254
+ :param value: New value to store; may be ``None``.
255
+ """
256
+ await self._store.set(self._key, value)
257
+
258
+ async def set_default(self) -> T | None:
259
+ """
260
+ Atomically persist *default* only if the key is missing.
261
+
262
+ :return: The existing value if present, otherwise the default.
263
+ """
264
+ return await self._store.set_default(self._key, self._default) # type: ignore[return-value]
265
+
266
+ async def delete(self) -> bool:
267
+ """
268
+ Delete the key from the store.
269
+
270
+ :return: ``True`` if the key existed.
271
+ """
272
+ return await self._store.delete_key(self._key)
273
+
274
+ async def has(self) -> bool:
275
+ """
276
+ Check whether the key exists.
277
+
278
+ :return: ``True`` if the key is present.
279
+ """
280
+ return await self._store.has(self._key)
@@ -10,6 +10,7 @@ from typing import Any, ClassVar, Self, Sequence, final
10
10
  import mongojet
11
11
  import msgspec
12
12
  from bson import ObjectId, int64
13
+ from msgspec._core import StructMeta
13
14
 
14
15
  from .operations import (
15
16
  CountOperationsMixin, DeleteOperationsMixin, FindOperationsMixin,
@@ -17,6 +18,20 @@ from .operations import (
17
18
  )
18
19
 
19
20
 
21
+ class _AutoKwOnlyMeta(StructMeta):
22
+ """Metaclass that propagates ``kw_only=True`` to all subclasses.
23
+
24
+ ``msgspec.Struct`` only inherits ``kw_only`` one level deep.
25
+ This metaclass ensures it is applied at every depth automatically,
26
+ unless explicitly overridden with ``kw_only=False``.
27
+ """
28
+
29
+ def __new__(mcs, name: str, bases: tuple[type, ...], namespace: dict[str, Any], **kwargs: Any) -> type:
30
+ if any(isinstance(b, type) and issubclass(b, msgspec.Struct) for b in bases):
31
+ kwargs.setdefault("kw_only", True)
32
+ return super().__new__(mcs, name, bases, namespace, **kwargs)
33
+
34
+
20
35
  def default_dec_hook(expected_type: type, obj: Any) -> Any:
21
36
  """Default decoding hook for basic type conversion.
22
37
 
@@ -56,7 +71,8 @@ class MongoDocument(
56
71
  FindOperationsMixin,
57
72
  InsertOperationsMixin,
58
73
  UpdateOperationsMixin,
59
- kw_only=True
74
+ metaclass=_AutoKwOnlyMeta,
75
+ kw_only=True,
60
76
  ):
61
77
  """
62
78
  Abstract base document for MongoDB collections with automatic binding.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mongospec
3
- Version: 0.2
3
+ Version: 0.2.2
4
4
  Summary: Async MongoDB ODM with msgspec integration and automatic collection binding
5
5
  Author-email: Diprog <diprog991@gmail.com>
6
6
  License-Expression: MIT
@@ -51,6 +51,7 @@ Minimal **async** MongoDB ODM built for *speed* and *simplicity*, featuring auto
51
51
  - [CRUD Operations](#crud-operations)
52
52
  - [Indexes](#indexes)
53
53
  - [Lifecycle Hooks](#lifecycle-hooks)
54
+ - [Contrib: KV Store](#contrib-kv-store)
54
55
  6. [Contributing](#contributing)
55
56
  7. [License](#license)
56
57
 
@@ -271,6 +272,67 @@ in sync — no caller-side boilerplate needed.
271
272
 
272
273
  ---
273
274
 
275
+ ### Contrib: KV Store
276
+
277
+ `mongospec.contrib.kv_store` provides a ready-made async key-value store
278
+ backed by a MongoDB collection. Designed for multiple inheritance with
279
+ project-specific base documents.
280
+
281
+ ```python
282
+ from mongospec.contrib.kv_store import KVStore, KVStoreItem
283
+ from myapp.db import Document # your base with timestamps, hooks, etc.
284
+
285
+
286
+ class AppStorage(KVStore, Document):
287
+ __collection_name__ = "app_storage"
288
+ ```
289
+
290
+ A unique index on `key` is created automatically at init time.
291
+
292
+ **Direct usage:**
293
+
294
+ ```python
295
+ await AppStorage.set("theme", "dark")
296
+ theme = await AppStorage.get("theme") # "dark"
297
+ theme = await AppStorage.get_or_default("x", 0) # 0 (no KeyError)
298
+ await AppStorage.set_default("theme", "light") # "dark" (atomic, no overwrite)
299
+
300
+ await AppStorage.set_many({"a": 1, "b": 2})
301
+ all_pairs = await AppStorage.get_all() # {"theme": "dark", "a": 1, "b": 2}
302
+ all_keys = await AppStorage.keys() # ["theme", "a", "b"]
303
+
304
+ await AppStorage.has("theme") # True
305
+ await AppStorage.delete_key("theme") # True
306
+ ```
307
+
308
+ **Typed accessor (`KVStoreItem`):**
309
+
310
+ ```python
311
+ AppStorageItem = KVStoreItem.of(AppStorage)
312
+
313
+ max_retries = AppStorageItem[int]("max_retries", default=3)
314
+
315
+ value = await max_retries.get() # 3 (default, not persisted)
316
+ await max_retries.set_default() # atomically persist default if missing
317
+ await max_retries.set(10)
318
+ await max_retries.has() # True
319
+ await max_retries.delete() # True
320
+ ```
321
+
322
+ | `KVStore` method | Description |
323
+ |------------------|-------------|
324
+ | `set(key, value)` | Upsert a value by key |
325
+ | `get(key)` | Get value or raise `KeyError` |
326
+ | `get_or_default(key, default)` | Get value or return default |
327
+ | `set_default(key, value)` | Atomic insert-if-absent (`$setOnInsert`) |
328
+ | `delete_key(key)` | Delete a key, return `True` if existed |
329
+ | `has(key)` | Check key existence |
330
+ | `get_all()` | Return all pairs as `dict` |
331
+ | `keys()` | Return all key names |
332
+ | `set_many(items)` | Upsert multiple pairs |
333
+
334
+ ---
335
+
274
336
  ## Contributing
275
337
 
276
338
  Contributions, issues and feature requests are welcome.
@@ -24,6 +24,8 @@ mongospec.egg-info/SOURCES.txt
24
24
  mongospec.egg-info/dependency_links.txt
25
25
  mongospec.egg-info/requires.txt
26
26
  mongospec.egg-info/top_level.txt
27
+ mongospec/contrib/__init__.py
28
+ mongospec/contrib/kv_store.py
27
29
  mongospec/document/__init__.py
28
30
  mongospec/document/document.py
29
31
  mongospec/document/operations/__init__.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mongospec"
3
- version = "0.2"
3
+ version = "0.2.2"
4
4
  authors = [
5
5
  {name = "Diprog", email = "diprog991@gmail.com"}
6
6
  ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes