scruby 0.2.0__py3-none-any.whl → 0.4.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
|
@@ -1,19 +1,4 @@
|
|
|
1
|
-
"""Creation and management of the database.
|
|
2
|
-
|
|
3
|
-
Examples:
|
|
4
|
-
>>> from scruby import Scruby
|
|
5
|
-
>>> db = Scruby()
|
|
6
|
-
>>> await db.set_key("key name", "Some text")
|
|
7
|
-
None
|
|
8
|
-
>>> await db.get_key("key name")
|
|
9
|
-
"Some text"
|
|
10
|
-
>>> await db.has_key("key name")
|
|
11
|
-
True
|
|
12
|
-
>>> await db.delete_key("key name")
|
|
13
|
-
None
|
|
14
|
-
>>> await db.napalm()
|
|
15
|
-
None
|
|
16
|
-
"""
|
|
1
|
+
"""Creation and management of the database."""
|
|
17
2
|
|
|
18
3
|
from __future__ import annotations
|
|
19
4
|
|
|
@@ -21,46 +6,33 @@ __all__ = ("Scruby",)
|
|
|
21
6
|
|
|
22
7
|
import hashlib
|
|
23
8
|
from shutil import rmtree
|
|
24
|
-
from typing import
|
|
9
|
+
from typing import Generic, TypeVar
|
|
25
10
|
|
|
26
11
|
import orjson
|
|
27
12
|
from anyio import Path, to_thread
|
|
28
13
|
|
|
29
|
-
|
|
14
|
+
T = TypeVar("T")
|
|
30
15
|
|
|
31
16
|
|
|
32
|
-
class Scruby:
|
|
17
|
+
class Scruby(Generic[T]): # noqa: UP046
|
|
33
18
|
"""Creation and management of the database.
|
|
34
19
|
|
|
35
|
-
Examples:
|
|
36
|
-
>>> from scruby import Scruby
|
|
37
|
-
>>> db = Scruby()
|
|
38
|
-
>>> await db.set_key("key name", "Some text")
|
|
39
|
-
None
|
|
40
|
-
>>> await db.get_key("key name")
|
|
41
|
-
"Some text"
|
|
42
|
-
>>> await db.has_key("key name")
|
|
43
|
-
True
|
|
44
|
-
>>> await db.delete_key("key name")
|
|
45
|
-
None
|
|
46
|
-
>>> await db.napalm()
|
|
47
|
-
None
|
|
48
|
-
|
|
49
20
|
Args:
|
|
50
|
-
|
|
21
|
+
db_name: Path to root directory of databases. By default = "ScrubyDB" (in root of project)
|
|
51
22
|
"""
|
|
52
23
|
|
|
53
24
|
def __init__( # noqa: D107
|
|
54
25
|
self,
|
|
55
|
-
|
|
26
|
+
class_model: T,
|
|
27
|
+
db_name: str = "ScrubyDB",
|
|
56
28
|
) -> None:
|
|
57
|
-
|
|
58
|
-
self.
|
|
29
|
+
self.__class_model = class_model
|
|
30
|
+
self.__db_name = db_name
|
|
59
31
|
|
|
60
32
|
@property
|
|
61
|
-
def
|
|
33
|
+
def db_name(self) -> str:
|
|
62
34
|
"""Get database name."""
|
|
63
|
-
return self.
|
|
35
|
+
return self.__db_name
|
|
64
36
|
|
|
65
37
|
async def get_leaf_path(self, key: str) -> Path:
|
|
66
38
|
"""Get the path to the database cell by key.
|
|
@@ -71,10 +43,10 @@ class Scruby:
|
|
|
71
43
|
# Key to md5 sum.
|
|
72
44
|
key_md5: str = hashlib.md5(key.encode("utf-8")).hexdigest() # noqa: S324
|
|
73
45
|
# Convert md5 sum in the segment of path.
|
|
74
|
-
|
|
46
|
+
separated_md5: str = "/".join(list(key_md5))
|
|
75
47
|
# The path of the branch to the database.
|
|
76
48
|
branch_path: Path = Path(
|
|
77
|
-
*(self.
|
|
49
|
+
*(self.__db_name, self.__class_model.__name__, separated_md5),
|
|
78
50
|
)
|
|
79
51
|
# If the branch does not exist, need to create it.
|
|
80
52
|
if not await branch_path.exists():
|
|
@@ -86,46 +58,31 @@ class Scruby:
|
|
|
86
58
|
async def set_key(
|
|
87
59
|
self,
|
|
88
60
|
key: str,
|
|
89
|
-
value:
|
|
61
|
+
value: T,
|
|
90
62
|
) -> None:
|
|
91
63
|
"""Asynchronous method for adding and updating keys to database.
|
|
92
64
|
|
|
93
|
-
Examples:
|
|
94
|
-
>>> from scruby import Scruby
|
|
95
|
-
>>> db = Scruby()
|
|
96
|
-
>>> await db.set_key("key name", "Some text")
|
|
97
|
-
None
|
|
98
|
-
|
|
99
65
|
Args:
|
|
100
66
|
key: Key name.
|
|
101
67
|
value: Value of key.
|
|
102
68
|
"""
|
|
103
69
|
# The path to the database cell.
|
|
104
70
|
leaf_path: Path = await self.get_leaf_path(key)
|
|
71
|
+
value_json: str = value.model_dump_json()
|
|
105
72
|
# Write key-value to the database.
|
|
106
73
|
if await leaf_path.exists():
|
|
107
74
|
# Add new key or update existing.
|
|
108
75
|
data_json: bytes = await leaf_path.read_bytes()
|
|
109
76
|
data: dict = orjson.loads(data_json) or {}
|
|
110
|
-
data[key] =
|
|
77
|
+
data[key] = value_json
|
|
111
78
|
await leaf_path.write_bytes(orjson.dumps(data))
|
|
112
79
|
else:
|
|
113
80
|
# Add new key to a blank leaf.
|
|
114
|
-
await leaf_path.write_bytes(
|
|
81
|
+
await leaf_path.write_bytes(orjson.dumps({key: value_json}))
|
|
115
82
|
|
|
116
|
-
async def get_key(self, key: str) ->
|
|
83
|
+
async def get_key(self, key: str) -> T:
|
|
117
84
|
"""Asynchronous method for getting key from database.
|
|
118
85
|
|
|
119
|
-
Examples:
|
|
120
|
-
>>> from scruby import Scruby
|
|
121
|
-
>>> db = Scruby()
|
|
122
|
-
>>> await db.set_key("key name", "Some text")
|
|
123
|
-
None
|
|
124
|
-
>>> await db.get_key("key name")
|
|
125
|
-
"Some text"
|
|
126
|
-
>>> await db.get_key("key missing")
|
|
127
|
-
KeyError
|
|
128
|
-
|
|
129
86
|
Args:
|
|
130
87
|
key: Key name.
|
|
131
88
|
"""
|
|
@@ -135,22 +92,13 @@ class Scruby:
|
|
|
135
92
|
if await leaf_path.exists():
|
|
136
93
|
data_json: bytes = await leaf_path.read_bytes()
|
|
137
94
|
data: dict = orjson.loads(data_json) or {}
|
|
138
|
-
|
|
95
|
+
obj: T = self.__class_model.model_validate_json(data[key])
|
|
96
|
+
return obj
|
|
139
97
|
raise KeyError()
|
|
140
98
|
|
|
141
99
|
async def has_key(self, key: str) -> bool:
|
|
142
100
|
"""Asynchronous method for checking presence of key in database.
|
|
143
101
|
|
|
144
|
-
Examples:
|
|
145
|
-
>>> from scruby import Scruby
|
|
146
|
-
>>> db = Scruby()
|
|
147
|
-
>>> await db.set_key("key name", "Some text")
|
|
148
|
-
None
|
|
149
|
-
>>> await db.has_key("key name")
|
|
150
|
-
True
|
|
151
|
-
>>> await db.has_key("key missing")
|
|
152
|
-
False
|
|
153
|
-
|
|
154
102
|
Args:
|
|
155
103
|
key: Key name.
|
|
156
104
|
"""
|
|
@@ -170,16 +118,6 @@ class Scruby:
|
|
|
170
118
|
async def delete_key(self, key: str) -> None:
|
|
171
119
|
"""Asynchronous method for deleting key from database.
|
|
172
120
|
|
|
173
|
-
Examples:
|
|
174
|
-
>>> from scruby import Scruby
|
|
175
|
-
>>> db = Scruby()
|
|
176
|
-
>>> await db.set_key("key name", "Some text")
|
|
177
|
-
None
|
|
178
|
-
>>> await db.delete_key("key name")
|
|
179
|
-
None
|
|
180
|
-
>>> await db.delete_key("key missing")
|
|
181
|
-
KeyError
|
|
182
|
-
|
|
183
121
|
Args:
|
|
184
122
|
key: Key name.
|
|
185
123
|
"""
|
|
@@ -195,20 +133,10 @@ class Scruby:
|
|
|
195
133
|
raise KeyError()
|
|
196
134
|
|
|
197
135
|
async def napalm(self) -> None:
|
|
198
|
-
"""Asynchronous method for full database deletion (Arg:
|
|
136
|
+
"""Asynchronous method for full database deletion (Arg: db_name).
|
|
199
137
|
|
|
200
138
|
Warning:
|
|
201
139
|
- `Be careful, this will remove all keys.`
|
|
202
|
-
|
|
203
|
-
Examples:
|
|
204
|
-
>>> from scruby import Scruby
|
|
205
|
-
>>> db = Scruby()
|
|
206
|
-
>>> await db.set_key("key name", "Some text")
|
|
207
|
-
None
|
|
208
|
-
>>> await db.napalm()
|
|
209
|
-
None
|
|
210
|
-
>>> await db.napalm()
|
|
211
|
-
FileNotFoundError
|
|
212
140
|
"""
|
|
213
|
-
await to_thread.run_sync(rmtree, self.
|
|
141
|
+
await to_thread.run_sync(rmtree, self.__db_name)
|
|
214
142
|
return
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scruby
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.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
|
|
@@ -11,7 +11,7 @@ Author-email: kebasyaty <kebasyaty@gmail.com>
|
|
|
11
11
|
License-Expression: MIT
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Keywords: database,db,scruby,store
|
|
14
|
-
Classifier: Development Status ::
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
16
16
|
Classifier: License :: OSI Approved :: MIT License
|
|
17
17
|
Classifier: Operating System :: MacOS :: MacOS X
|
|
@@ -26,7 +26,8 @@ Classifier: Topic :: Database
|
|
|
26
26
|
Classifier: Typing :: Typed
|
|
27
27
|
Requires-Python: <4.0,>=3.12
|
|
28
28
|
Requires-Dist: anyio>=4.10.0
|
|
29
|
-
Requires-Dist: orjson>=3.11.
|
|
29
|
+
Requires-Dist: orjson>=3.11.3
|
|
30
|
+
Requires-Dist: pydantic>=2.11.7
|
|
30
31
|
Description-Content-Type: text/markdown
|
|
31
32
|
|
|
32
33
|
<div align="center">
|
|
@@ -94,20 +95,49 @@ uv add scruby
|
|
|
94
95
|
|
|
95
96
|
```python
|
|
96
97
|
import anyio
|
|
98
|
+
import datetime
|
|
99
|
+
from pydantic import BaseModel
|
|
97
100
|
from scruby import Scruby
|
|
98
101
|
|
|
99
102
|
|
|
100
103
|
async def main() -> None:
|
|
101
104
|
"""Example."""
|
|
102
105
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
class User(BaseModel):
|
|
107
|
+
"""User model."""
|
|
108
|
+
|
|
109
|
+
first_name: str
|
|
110
|
+
last_name: str
|
|
111
|
+
birthday: datetime.datetime
|
|
112
|
+
email: str
|
|
113
|
+
phone: str
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
db = Scruby(
|
|
117
|
+
class_model=User,
|
|
118
|
+
db_name="ScrubyDB", # By default = "ScrubyDB"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
user = User(
|
|
122
|
+
first_name="John",
|
|
123
|
+
last_name="Smith",
|
|
124
|
+
birthday=datetime.datetime(1970, 1, 1), # noqa: DTZ001
|
|
125
|
+
email="John_Smith@gmail.com",
|
|
126
|
+
phone="+447986123456",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
await db.set_key("+447986123456", user)
|
|
130
|
+
|
|
131
|
+
await db.get_key("+447986123456") # => user
|
|
106
132
|
await db.get_key("key missing") # => KeyError
|
|
107
|
-
|
|
133
|
+
|
|
134
|
+
await db.has_key("+447986123456") # => True
|
|
108
135
|
await db.has_key("key missing") # => False
|
|
109
|
-
|
|
136
|
+
|
|
137
|
+
await db.delete_key("+447986123456")
|
|
138
|
+
await db.delete_key("+447986123456") # => KeyError
|
|
110
139
|
await db.delete_key("key missing") # => KeyError
|
|
140
|
+
|
|
111
141
|
# Full database deletion.
|
|
112
142
|
await db.napalm()
|
|
113
143
|
await db.napalm() # => FileNotFoundError
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
scruby/__init__.py,sha256=TCxUjBI5A0KZcwvfmgaBVl8ScuzzOVvALl_T4iqSR9c,603
|
|
2
|
+
scruby/db.py,sha256=86e-U3IvNADoYhHv4PHGikqnH4KWVJEpu3d7oGuUc3s,4600
|
|
3
|
+
scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
scruby-0.4.0.dist-info/METADATA,sha256=Oi3khca39iX9qdOuF-SEMtT6rkbESwDS8Wy3Zhjo8-o,6512
|
|
5
|
+
scruby-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
+
scruby-0.4.0.dist-info/licenses/LICENSE,sha256=2zZINd6m_jNYlowdQImlEizyhSui5cBAJZRhWQURcEc,1095
|
|
7
|
+
scruby-0.4.0.dist-info/RECORD,,
|
scruby-0.2.0.dist-info/RECORD
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
scruby/__init__.py,sha256=TCxUjBI5A0KZcwvfmgaBVl8ScuzzOVvALl_T4iqSR9c,603
|
|
2
|
-
scruby/db.py,sha256=otENL-t7ie_8Fgzcteh2WsXFPu9pM4DBUdO_2GB1vwk,6565
|
|
3
|
-
scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
scruby-0.2.0.dist-info/METADATA,sha256=AuMbKEINUsGEuy48SScAAhYr-siqKvr8P24IcpDg0B0,5919
|
|
5
|
-
scruby-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
scruby-0.2.0.dist-info/licenses/LICENSE,sha256=2zZINd6m_jNYlowdQImlEizyhSui5cBAJZRhWQURcEc,1095
|
|
7
|
-
scruby-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|