scruby 0.9.0__py3-none-any.whl → 0.17.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.
- scruby/__init__.py +29 -21
- scruby/aggregation.py +148 -0
- scruby/constants.py +31 -31
- scruby/db.py +756 -290
- scruby/errors.py +39 -0
- {scruby-0.9.0.dist-info → scruby-0.17.0.dist-info}/METADATA +43 -69
- scruby-0.17.0.dist-info/RECORD +10 -0
- {scruby-0.9.0.dist-info → scruby-0.17.0.dist-info}/licenses/LICENSE +21 -21
- scruby-0.9.0.dist-info/RECORD +0 -8
- {scruby-0.9.0.dist-info → scruby-0.17.0.dist-info}/WHEEL +0 -0
scruby/errors.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Scruby Exceptions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = (
|
|
6
|
+
"ScrubyException",
|
|
7
|
+
"MetadataValueError",
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ScrubyException(Exception):
|
|
12
|
+
"""Root Custom Exception."""
|
|
13
|
+
|
|
14
|
+
def __init__(self, *args, **kwargs) -> None: # type: ignore[no-untyped-def] # noqa: D107
|
|
15
|
+
super().__init__(*args, **kwargs)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MetadataValueError(ScrubyException):
|
|
19
|
+
"""Exception is raised if value of variable in metadata does not matching expected."""
|
|
20
|
+
|
|
21
|
+
def __init__(self, message: str) -> None: # noqa: D107
|
|
22
|
+
self.message = message
|
|
23
|
+
super().__init__(self.message)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class KeyAlreadyExistsError(ScrubyException):
|
|
27
|
+
"""Exception is raised if the key already exists."""
|
|
28
|
+
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
self.message = "The key already exists."
|
|
31
|
+
super().__init__(self.message)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class KeyNotExistsError(ScrubyException):
|
|
35
|
+
"""Exception is raised If the key is not exists."""
|
|
36
|
+
|
|
37
|
+
def __init__(self) -> None:
|
|
38
|
+
self.message = "The key not exists."
|
|
39
|
+
super().__init__(self.message)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scruby
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.17.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
|
|
@@ -21,6 +21,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
21
21
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
22
|
Classifier: Programming Language :: Python :: 3.12
|
|
23
23
|
Classifier: Programming Language :: Python :: 3.13
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
24
25
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
25
26
|
Classifier: Topic :: Database
|
|
26
27
|
Classifier: Typing :: Typed
|
|
@@ -43,7 +44,7 @@ Description-Content-Type: text/markdown
|
|
|
43
44
|
</p>
|
|
44
45
|
<p>
|
|
45
46
|
<h1>Scruby</h1>
|
|
46
|
-
<h3>
|
|
47
|
+
<h3>Asynchronous library for building and managing a hybrid database,<br>by scheme of key-value.</h3>
|
|
47
48
|
<p align="center">
|
|
48
49
|
<a href="https://github.com/kebasyaty/scruby/actions/workflows/test.yml" alt="Build Status"><img src="https://github.com/kebasyaty/scruby/actions/workflows/test.yml/badge.svg" alt="Build Status"></a>
|
|
49
50
|
<a href="https://kebasyaty.github.io/scruby/" alt="Docs"><img src="https://img.shields.io/badge/docs-available-brightgreen.svg" alt="Docs"></a>
|
|
@@ -51,34 +52,30 @@ Description-Content-Type: text/markdown
|
|
|
51
52
|
<a href="https://pypi.python.org/pypi/scruby/" alt="PyPI status"><img src="https://img.shields.io/pypi/status/scruby.svg" alt="PyPI status"></a>
|
|
52
53
|
<a href="https://pypi.python.org/pypi/scruby/" alt="PyPI version fury.io"><img src="https://badge.fury.io/py/scruby.svg" alt="PyPI version fury.io"></a>
|
|
53
54
|
<br>
|
|
54
|
-
<a href="https://github.com/kebasyaty/scruby/issues"><img src="https://img.shields.io/github/issues/kebasyaty/scruby.svg" alt="GitHub issues"></a>
|
|
55
|
-
<a href="https://pepy.tech/projects/scruby"><img src="https://static.pepy.tech/badge/scruby" alt="PyPI Downloads"></a>
|
|
56
|
-
<a href="https://github.com/kebasyaty/scruby/blob/main/LICENSE" alt="GitHub license"><img src="https://img.shields.io/github/license/kebasyaty/scruby" alt="GitHub license"></a>
|
|
57
55
|
<a href="https://mypy-lang.org/" alt="Types: Mypy"><img src="https://img.shields.io/badge/types-Mypy-202235.svg?color=0c7ebf" alt="Types: Mypy"></a>
|
|
58
56
|
<a href="https://docs.astral.sh/ruff/" alt="Code style: Ruff"><img src="https://img.shields.io/badge/code%20style-Ruff-FDD835.svg" alt="Code style: Ruff"></a>
|
|
59
|
-
<a href="https://github.com/kebasyaty/scruby" alt="PyPI implementation"><img src="https://img.shields.io/pypi/implementation/scruby" alt="PyPI implementation"></a>
|
|
60
|
-
<br>
|
|
61
57
|
<a href="https://pypi.org/project/scruby"><img src="https://img.shields.io/pypi/format/scruby" alt="Format"></a>
|
|
62
|
-
<a href="https://
|
|
63
|
-
<a href="https://github.com/kebasyaty/scruby"><img src="https://img.shields.io/github/
|
|
64
|
-
<a href="https://github.com/kebasyaty/scruby"><img src="https://img.shields.io/github/last-commit/kebasyaty/scruby/main" alt="Last commit"></a>
|
|
65
|
-
<a href="https://github.com/kebasyaty/scruby/releases/" alt="GitHub release"><img src="https://img.shields.io/github/release/kebasyaty/scruby" alt="GitHub release"></a>
|
|
58
|
+
<a href="https://pepy.tech/projects/scruby"><img src="https://static.pepy.tech/badge/scruby" alt="PyPI Downloads"></a>
|
|
59
|
+
<a href="https://github.com/kebasyaty/scruby/blob/main/LICENSE" alt="GitHub license"><img src="https://img.shields.io/github/license/kebasyaty/scruby" alt="GitHub license"></a>
|
|
66
60
|
</p>
|
|
67
61
|
<p align="center">
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
62
|
+
The library uses fractal-tree addressing and
|
|
63
|
+
<br>
|
|
64
|
+
the search for documents based on the effect of a quantum loop.
|
|
65
|
+
<br>
|
|
66
|
+
The database consists of collections.
|
|
67
|
+
<br>
|
|
68
|
+
The maximum size of the one collection is 16\*\*8=4294967296 branches,
|
|
69
|
+
<br>
|
|
70
|
+
each branch can store one or more keys.
|
|
71
|
+
<br>
|
|
72
|
+
The value of any key in collection can be obtained in 8 steps,
|
|
73
|
+
<br>
|
|
74
|
+
thereby achieving high performance.
|
|
75
|
+
<br>
|
|
76
|
+
The effectiveness of the search for documents based on a quantum loop,
|
|
77
|
+
<br>
|
|
78
|
+
requires a large number of processor threads.
|
|
82
79
|
</p>
|
|
83
80
|
</p>
|
|
84
81
|
</div>
|
|
@@ -103,6 +100,8 @@ uv add scruby
|
|
|
103
100
|
|
|
104
101
|
## Usage
|
|
105
102
|
|
|
103
|
+
See more examples here [https://kebasyaty.github.io/scruby/latest/pages/usage/](https://kebasyaty.github.io/scruby/latest/pages/usage/ "Examples").
|
|
104
|
+
|
|
106
105
|
```python
|
|
107
106
|
"""Working with keys."""
|
|
108
107
|
|
|
@@ -115,6 +114,7 @@ from scruby import Scruby, constants
|
|
|
115
114
|
|
|
116
115
|
constants.DB_ROOT = "ScrubyDB" # By default = "ScrubyDB"
|
|
117
116
|
|
|
117
|
+
|
|
118
118
|
class User(BaseModel):
|
|
119
119
|
"""Model of User."""
|
|
120
120
|
first_name: str
|
|
@@ -123,6 +123,7 @@ class User(BaseModel):
|
|
|
123
123
|
email: EmailStr
|
|
124
124
|
phone: Annotated[PhoneNumber, PhoneNumberValidator(number_format="E164")]
|
|
125
125
|
|
|
126
|
+
|
|
126
127
|
async def main() -> None:
|
|
127
128
|
"""Example."""
|
|
128
129
|
# Get collection of `User`.
|
|
@@ -136,7 +137,9 @@ async def main() -> None:
|
|
|
136
137
|
phone="+447986123456",
|
|
137
138
|
)
|
|
138
139
|
|
|
139
|
-
await user_coll.
|
|
140
|
+
await user_coll.add_key(user.phone, user)
|
|
141
|
+
|
|
142
|
+
await user_coll.update_key(user.phone, user)
|
|
140
143
|
|
|
141
144
|
await user_coll.get_key("+447986123456") # => user
|
|
142
145
|
await user_coll.get_key("key missing") # => KeyError
|
|
@@ -152,12 +155,13 @@ async def main() -> None:
|
|
|
152
155
|
# Hint: The main purpose is tests.
|
|
153
156
|
await Scruby.napalm()
|
|
154
157
|
|
|
158
|
+
|
|
155
159
|
if __name__ == "__main__":
|
|
156
160
|
anyio.run(main)
|
|
157
161
|
```
|
|
158
162
|
|
|
159
163
|
```python
|
|
160
|
-
"""Find a single document.
|
|
164
|
+
"""Find a single document matching the filter.
|
|
161
165
|
|
|
162
166
|
The search is based on the effect of a quantum loop.
|
|
163
167
|
The search effectiveness depends on the number of processor threads.
|
|
@@ -173,8 +177,9 @@ from scruby import Scruby, constants
|
|
|
173
177
|
from pprint import pprint as pp
|
|
174
178
|
|
|
175
179
|
constants.DB_ROOT = "ScrubyDB" # By default = "ScrubyDB"
|
|
176
|
-
constants.
|
|
177
|
-
|
|
180
|
+
constants.HASH_REDUCE_LEFT = 6 # 256 branches in collection
|
|
181
|
+
# (main purpose is tests).
|
|
182
|
+
|
|
178
183
|
|
|
179
184
|
class User(BaseModel):
|
|
180
185
|
"""Model of User."""
|
|
@@ -184,6 +189,7 @@ class User(BaseModel):
|
|
|
184
189
|
email: EmailStr
|
|
185
190
|
phone: Annotated[PhoneNumber, PhoneNumberValidator(number_format="E164")]
|
|
186
191
|
|
|
192
|
+
|
|
187
193
|
async def main() -> None:
|
|
188
194
|
"""Example."""
|
|
189
195
|
# Get collection of `User`.
|
|
@@ -199,7 +205,7 @@ async def main() -> None:
|
|
|
199
205
|
)
|
|
200
206
|
|
|
201
207
|
# Add user to collection.
|
|
202
|
-
await user_coll.
|
|
208
|
+
await user_coll.add_key(user.phone, user)
|
|
203
209
|
|
|
204
210
|
# Find user by email.
|
|
205
211
|
user_details: User | None = user_coll.find_one(
|
|
@@ -223,12 +229,13 @@ async def main() -> None:
|
|
|
223
229
|
# Hint: The main purpose is tests.
|
|
224
230
|
await Scruby.napalm()
|
|
225
231
|
|
|
232
|
+
|
|
226
233
|
if __name__ == "__main__":
|
|
227
234
|
anyio.run(main)
|
|
228
235
|
```
|
|
229
236
|
|
|
230
237
|
```python
|
|
231
|
-
"""Find documents.
|
|
238
|
+
"""Find one or more documents matching the filter.
|
|
232
239
|
|
|
233
240
|
The search is based on the effect of a quantum loop.
|
|
234
241
|
The search effectiveness depends on the number of processor threads.
|
|
@@ -244,8 +251,9 @@ from scruby import Scruby, constants
|
|
|
244
251
|
from pprint import pprint as pp
|
|
245
252
|
|
|
246
253
|
constants.DB_ROOT = "ScrubyDB" # By default = "ScrubyDB"
|
|
247
|
-
constants.
|
|
248
|
-
|
|
254
|
+
constants.HASH_REDUCE_LEFT = 6 # 256 branches in collection
|
|
255
|
+
# (main purpose is tests).
|
|
256
|
+
|
|
249
257
|
|
|
250
258
|
class User(BaseModel):
|
|
251
259
|
"""Model of User."""
|
|
@@ -255,6 +263,7 @@ class User(BaseModel):
|
|
|
255
263
|
email: EmailStr
|
|
256
264
|
phone: Annotated[PhoneNumber, PhoneNumberValidator(number_format="E164")]
|
|
257
265
|
|
|
266
|
+
|
|
258
267
|
async def main() -> None:
|
|
259
268
|
"""Example."""
|
|
260
269
|
# Get collection of `User`.
|
|
@@ -269,7 +278,7 @@ async def main() -> None:
|
|
|
269
278
|
email=f"John_Smith_{num}@gmail.com",
|
|
270
279
|
phone=f"+44798612345{num}",
|
|
271
280
|
)
|
|
272
|
-
await
|
|
281
|
+
await user_coll.add_key(user.phone, user)
|
|
273
282
|
|
|
274
283
|
# Find users by email.
|
|
275
284
|
users: list[User] | None = user_coll.find_many(
|
|
@@ -284,41 +293,6 @@ async def main() -> None:
|
|
|
284
293
|
# Hint: The main purpose is tests.
|
|
285
294
|
await Scruby.napalm()
|
|
286
295
|
|
|
287
|
-
if __name__ == "__main__":
|
|
288
|
-
anyio.run(main)
|
|
289
|
-
```
|
|
290
|
-
|
|
291
|
-
```python
|
|
292
|
-
"""Get collection name."""
|
|
293
|
-
|
|
294
|
-
import anyio
|
|
295
|
-
import datetime
|
|
296
|
-
from typing import Annotated
|
|
297
|
-
from pydantic import BaseModel, EmailStr
|
|
298
|
-
from pydantic_extra_types.phone_numbers import PhoneNumber, PhoneNumberValidator
|
|
299
|
-
from scruby import Scruby, constants
|
|
300
|
-
|
|
301
|
-
constants.DB_ROOT = "ScrubyDB" # By default = "ScrubyDB"
|
|
302
|
-
|
|
303
|
-
class User(BaseModel):
|
|
304
|
-
"""Model of User."""
|
|
305
|
-
first_name: str
|
|
306
|
-
last_name: str
|
|
307
|
-
birthday: datetime.datetime
|
|
308
|
-
email: EmailStr
|
|
309
|
-
phone: Annotated[PhoneNumber, PhoneNumberValidator(number_format="E164")]
|
|
310
|
-
|
|
311
|
-
async def main() -> None:
|
|
312
|
-
"""Example."""
|
|
313
|
-
# Get collection of `User`.
|
|
314
|
-
user_coll = Scruby(User)
|
|
315
|
-
|
|
316
|
-
print(user_coll.collection_name()) # "User"
|
|
317
|
-
print(user_coll.collection_full_name()) # "ScrubyDB/User"
|
|
318
|
-
|
|
319
|
-
# Full database deletion.
|
|
320
|
-
# Hint: The main purpose is tests.
|
|
321
|
-
await Scruby.napalm()
|
|
322
296
|
|
|
323
297
|
if __name__ == "__main__":
|
|
324
298
|
anyio.run(main)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
scruby/__init__.py,sha256=elrW_AWMyl3kuTpEqGPaYFSpF8iVzjpivF6MxVNlqoQ,855
|
|
2
|
+
scruby/aggregation.py,sha256=SYGcnMy2eq9vJb-pW3xR9LLAQIQ55TK-LGW_yKQ-7sU,3318
|
|
3
|
+
scruby/constants.py,sha256=KRx7naXDNzT1WVKiYXFJ8uYxS9-aSqoiilsa4qR6y1Y,976
|
|
4
|
+
scruby/db.py,sha256=Ml2A46bOX1P1sF4vKSyEFqklMAjoSpn1lGvj8VCtnSY,26323
|
|
5
|
+
scruby/errors.py,sha256=nMhSLAKo7F3lTB_lgOZ5ksMuBtA1ugHqF8iyp1EY-sY,1042
|
|
6
|
+
scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
scruby-0.17.0.dist-info/METADATA,sha256=8RMszCN_EnAzMTN3wpDrIh8BrrdlxYTX__pLyy6siz8,10079
|
|
8
|
+
scruby-0.17.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
9
|
+
scruby-0.17.0.dist-info/licenses/LICENSE,sha256=mS0Wz0yGNB63gEcWEnuIb_lldDYV0sjRaO-o_GL6CWE,1074
|
|
10
|
+
scruby-0.17.0.dist-info/RECORD,,
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Gennady Kostyunin
|
|
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
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Gennady Kostyunin
|
|
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.
|
scruby-0.9.0.dist-info/RECORD
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
scruby/__init__.py,sha256=myX7sG-7oAQZGdgfZtTGXYCCraTeuwi7SjBoltftpnM,648
|
|
2
|
-
scruby/constants.py,sha256=GbB-O0qaVdi5EHUp-zRAppFXLR-oHxpXUFVAOCpS0C8,1022
|
|
3
|
-
scruby/db.py,sha256=1tfDCwoKwUx0a6DfEOpPAV8NYuifmmrX8Q9gU3K6l58,10407
|
|
4
|
-
scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
-
scruby-0.9.0.dist-info/METADATA,sha256=7Durmb2_VtJ4plz-OM-vhzfLAM8QGYkmVTRX1KFfOYY,11533
|
|
6
|
-
scruby-0.9.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
-
scruby-0.9.0.dist-info/licenses/LICENSE,sha256=2zZINd6m_jNYlowdQImlEizyhSui5cBAJZRhWQURcEc,1095
|
|
8
|
-
scruby-0.9.0.dist-info/RECORD,,
|
|
File without changes
|