scruby 0.9.2__py3-none-any.whl → 0.10.0__py3-none-any.whl
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.
Potentially problematic release.
This version of scruby might be problematic. Click here for more details.
scruby/db.py
CHANGED
|
@@ -6,6 +6,7 @@ __all__ = ("Scruby",)
|
|
|
6
6
|
|
|
7
7
|
import concurrent.futures
|
|
8
8
|
import contextlib
|
|
9
|
+
import datetime
|
|
9
10
|
import logging
|
|
10
11
|
import zlib
|
|
11
12
|
from collections.abc import Callable
|
|
@@ -15,6 +16,7 @@ from typing import Any, Never, TypeVar, assert_never
|
|
|
15
16
|
|
|
16
17
|
import orjson
|
|
17
18
|
from anyio import Path, to_thread
|
|
19
|
+
from pydantic import BaseModel
|
|
18
20
|
|
|
19
21
|
from scruby import constants
|
|
20
22
|
|
|
@@ -23,6 +25,14 @@ logger = logging.getLogger(__name__)
|
|
|
23
25
|
T = TypeVar("T")
|
|
24
26
|
|
|
25
27
|
|
|
28
|
+
class _Meta(BaseModel):
|
|
29
|
+
"""Metadata of collection."""
|
|
30
|
+
|
|
31
|
+
count_documents: int
|
|
32
|
+
created_at: datetime.datetime
|
|
33
|
+
updated_at: datetime.datetime
|
|
34
|
+
|
|
35
|
+
|
|
26
36
|
class Scruby[T]:
|
|
27
37
|
"""Creation and management of database.
|
|
28
38
|
|
|
@@ -34,6 +44,7 @@ class Scruby[T]:
|
|
|
34
44
|
self,
|
|
35
45
|
class_model: T,
|
|
36
46
|
) -> None:
|
|
47
|
+
self.__meta = _Meta
|
|
37
48
|
self.__class_model = class_model
|
|
38
49
|
self.__db_root = constants.DB_ROOT
|
|
39
50
|
self.__length_reduction_hash = constants.LENGTH_REDUCTION_HASH
|
|
@@ -51,10 +62,76 @@ class Scruby[T]:
|
|
|
51
62
|
msg: str = f"{unreachable} - Unacceptable value for LENGTH_REDUCTION_HASH."
|
|
52
63
|
logger.critical(msg)
|
|
53
64
|
assert_never(Never(unreachable))
|
|
65
|
+
# Create metadata if absent.
|
|
66
|
+
self._create_metadata()
|
|
67
|
+
|
|
68
|
+
def _create_metadata(self) -> None:
|
|
69
|
+
"""Create metadata for collection if absent.
|
|
70
|
+
|
|
71
|
+
This method is for internal use.
|
|
72
|
+
"""
|
|
73
|
+
key: int = 0
|
|
74
|
+
key_as_hash: str = f"{key:08x}"[self.__length_reduction_hash :]
|
|
75
|
+
separated_hash: str = "/".join(list(key_as_hash))
|
|
76
|
+
branch_path = SyncPath(
|
|
77
|
+
*(
|
|
78
|
+
self.__db_root,
|
|
79
|
+
self.__class_model.__name__,
|
|
80
|
+
separated_hash,
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
if not branch_path.exists():
|
|
84
|
+
branch_path.mkdir(parents=True)
|
|
85
|
+
meta = _Meta(
|
|
86
|
+
count_documents=0,
|
|
87
|
+
created_at=datetime.datetime.now(), # noqa: DTZ005
|
|
88
|
+
updated_at=datetime.datetime.now(), # noqa: DTZ005
|
|
89
|
+
)
|
|
90
|
+
meta_json = meta.model_dump_json()
|
|
91
|
+
meta_path = SyncPath(*(branch_path, "meta.json"))
|
|
92
|
+
meta_path.write_text(meta_json, "utf-8")
|
|
93
|
+
|
|
94
|
+
async def _get_meta_path(self) -> Path:
|
|
95
|
+
"""Asynchronous method for getting path to metadata of collection.
|
|
96
|
+
|
|
97
|
+
This method is for internal use.
|
|
98
|
+
"""
|
|
99
|
+
key: int = 0
|
|
100
|
+
key_as_hash: str = f"{key:08x}"[self.__length_reduction_hash :]
|
|
101
|
+
separated_hash: str = "/".join(list(key_as_hash))
|
|
102
|
+
return Path(
|
|
103
|
+
*(
|
|
104
|
+
self.__db_root,
|
|
105
|
+
self.__class_model.__name__,
|
|
106
|
+
separated_hash,
|
|
107
|
+
"meta.json",
|
|
108
|
+
),
|
|
109
|
+
)
|
|
54
110
|
|
|
55
|
-
async def
|
|
111
|
+
async def _get_meta(self) -> _Meta:
|
|
112
|
+
"""Asynchronous method for getting metadata of collection.
|
|
113
|
+
|
|
114
|
+
This method is for internal use.
|
|
115
|
+
"""
|
|
116
|
+
meta_path = await self._get_meta_path()
|
|
117
|
+
meta_json = await meta_path.read_text()
|
|
118
|
+
meta: _Meta = self.__meta.model_validate_json(meta_json)
|
|
119
|
+
return meta
|
|
120
|
+
|
|
121
|
+
async def _set_meta(self, meta: _Meta) -> None:
|
|
122
|
+
"""Asynchronous method for updating metadata of collection.
|
|
123
|
+
|
|
124
|
+
This method is for internal use.
|
|
125
|
+
"""
|
|
126
|
+
meta_path = await self._get_meta_path()
|
|
127
|
+
meta_json = meta.model_dump_json()
|
|
128
|
+
await meta_path.write_text(meta_json, "utf-8")
|
|
129
|
+
|
|
130
|
+
async def _get_leaf_path(self, key: str) -> Path:
|
|
56
131
|
"""Asynchronous method for getting path to collection cell by key.
|
|
57
132
|
|
|
133
|
+
This method is for internal use.
|
|
134
|
+
|
|
58
135
|
Args:
|
|
59
136
|
key: Key name.
|
|
60
137
|
"""
|
|
@@ -95,7 +172,7 @@ class Scruby[T]:
|
|
|
95
172
|
value: Value of key.
|
|
96
173
|
"""
|
|
97
174
|
# The path to the database cell.
|
|
98
|
-
leaf_path: Path = await self.
|
|
175
|
+
leaf_path: Path = await self._get_leaf_path(key)
|
|
99
176
|
value_json: str = value.model_dump_json()
|
|
100
177
|
# Write key-value to the database.
|
|
101
178
|
if await leaf_path.exists():
|
|
@@ -115,7 +192,7 @@ class Scruby[T]:
|
|
|
115
192
|
key: Key name.
|
|
116
193
|
"""
|
|
117
194
|
# The path to the database cell.
|
|
118
|
-
leaf_path: Path = await self.
|
|
195
|
+
leaf_path: Path = await self._get_leaf_path(key)
|
|
119
196
|
# Get value of key.
|
|
120
197
|
if await leaf_path.exists():
|
|
121
198
|
data_json: bytes = await leaf_path.read_bytes()
|
|
@@ -133,7 +210,7 @@ class Scruby[T]:
|
|
|
133
210
|
key: Key name.
|
|
134
211
|
"""
|
|
135
212
|
# The path to the database cell.
|
|
136
|
-
leaf_path: Path = await self.
|
|
213
|
+
leaf_path: Path = await self._get_leaf_path(key)
|
|
137
214
|
# Checking whether there is a key.
|
|
138
215
|
if await leaf_path.exists():
|
|
139
216
|
data_json: bytes = await leaf_path.read_bytes()
|
|
@@ -152,7 +229,7 @@ class Scruby[T]:
|
|
|
152
229
|
key: Key name.
|
|
153
230
|
"""
|
|
154
231
|
# The path to the database cell.
|
|
155
|
-
leaf_path: Path = await self.
|
|
232
|
+
leaf_path: Path = await self._get_leaf_path(key)
|
|
156
233
|
# Deleting key.
|
|
157
234
|
if await leaf_path.exists():
|
|
158
235
|
data_json: bytes = await leaf_path.read_bytes()
|
|
@@ -178,14 +255,17 @@ class Scruby[T]:
|
|
|
178
255
|
return
|
|
179
256
|
|
|
180
257
|
@staticmethod
|
|
181
|
-
def
|
|
258
|
+
def _search_task(
|
|
182
259
|
key: int,
|
|
183
260
|
filter_fn: Callable,
|
|
184
261
|
length_reduction_hash: str,
|
|
185
262
|
db_root: str,
|
|
186
263
|
class_model: T,
|
|
187
264
|
) -> dict[str, Any] | None:
|
|
188
|
-
"""
|
|
265
|
+
"""Task for searching for documents.
|
|
266
|
+
|
|
267
|
+
This method is for internal use.
|
|
268
|
+
"""
|
|
189
269
|
key_as_hash: str = f"{key:08x}"[length_reduction_hash:]
|
|
190
270
|
separated_hash: str = "/".join(list(key_as_hash))
|
|
191
271
|
leaf_path: SyncPath = SyncPath(
|
|
@@ -226,7 +306,7 @@ class Scruby[T]:
|
|
|
226
306
|
If None, then there is no limit on the wait time.
|
|
227
307
|
"""
|
|
228
308
|
keys: range = range(1, self.__max_num_keys)
|
|
229
|
-
search_task_fn: Callable = self.
|
|
309
|
+
search_task_fn: Callable = self._search_task
|
|
230
310
|
length_reduction_hash: int = self.__length_reduction_hash
|
|
231
311
|
db_root: str = self.__db_root
|
|
232
312
|
class_model: T = self.__class_model
|
|
@@ -245,7 +325,7 @@ class Scruby[T]:
|
|
|
245
325
|
return doc
|
|
246
326
|
return None
|
|
247
327
|
|
|
248
|
-
def
|
|
328
|
+
def find(
|
|
249
329
|
self,
|
|
250
330
|
filter_fn: Callable,
|
|
251
331
|
db_query_docs_limit: int = 1000,
|
|
@@ -268,7 +348,7 @@ class Scruby[T]:
|
|
|
268
348
|
If None, then there is no limit on the wait time.
|
|
269
349
|
"""
|
|
270
350
|
keys: range = range(1, self.__max_num_keys)
|
|
271
|
-
search_task_fn: Callable = self.
|
|
351
|
+
search_task_fn: Callable = self._search_task
|
|
272
352
|
length_reduction_hash: int = self.__length_reduction_hash
|
|
273
353
|
db_root: str = self.__db_root
|
|
274
354
|
class_model: T = self.__class_model
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scruby
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: A fast key-value storage library.
|
|
5
5
|
Project-URL: Homepage, https://github.com/kebasyaty/scruby
|
|
6
6
|
Project-URL: Repository, https://github.com/kebasyaty/scruby
|
|
@@ -274,7 +274,7 @@ async def main() -> None:
|
|
|
274
274
|
await db.set_key(f"+44798612345{num}", user)
|
|
275
275
|
|
|
276
276
|
# Find users by email.
|
|
277
|
-
users: list[User] | None = user_coll.
|
|
277
|
+
users: list[User] | None = user_coll.find(
|
|
278
278
|
filter_fn=lambda doc: doc.email == "John_Smith_5@gmail.com" or doc.email == "John_Smith_8@gmail.com",
|
|
279
279
|
)
|
|
280
280
|
if users is not None:
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
scruby/__init__.py,sha256=wFwUS1KcLxfIopXOVS8gPue9fNzIIU2cVj_RgK5drz4,849
|
|
2
|
+
scruby/constants.py,sha256=GbB-O0qaVdi5EHUp-zRAppFXLR-oHxpXUFVAOCpS0C8,1022
|
|
3
|
+
scruby/db.py,sha256=kwF1X_HB2CaNGNY0DVrC6s8OQ5Iix1azQOmfA_10JBo,13563
|
|
4
|
+
scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
scruby-0.10.0.dist-info/METADATA,sha256=seuAAwZHI-iso9O4durUh4dSye1KQrSZ8MjTkyOvCq8,10829
|
|
6
|
+
scruby-0.10.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
+
scruby-0.10.0.dist-info/licenses/LICENSE,sha256=2zZINd6m_jNYlowdQImlEizyhSui5cBAJZRhWQURcEc,1095
|
|
8
|
+
scruby-0.10.0.dist-info/RECORD,,
|
scruby-0.9.2.dist-info/RECORD
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
scruby/__init__.py,sha256=wFwUS1KcLxfIopXOVS8gPue9fNzIIU2cVj_RgK5drz4,849
|
|
2
|
-
scruby/constants.py,sha256=GbB-O0qaVdi5EHUp-zRAppFXLR-oHxpXUFVAOCpS0C8,1022
|
|
3
|
-
scruby/db.py,sha256=W3VvC3ss1NXonQnFI6Yw2MobBARMgus5Acw-k5uL3TE,10946
|
|
4
|
-
scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
scruby-0.9.2.dist-info/METADATA,sha256=vZab1ietT1of_GXLHEISYhIkXZJuBIp3vk_rAFGeNPA,10833
|
|
6
|
-
scruby-0.9.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
-
scruby-0.9.2.dist-info/licenses/LICENSE,sha256=2zZINd6m_jNYlowdQImlEizyhSui5cBAJZRhWQURcEc,1095
|
|
8
|
-
scruby-0.9.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|