scruby 0.3.0__py3-none-any.whl → 0.5.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/__init__.py +5 -1
- scruby/constants.py +11 -0
- scruby/db.py +22 -97
- {scruby-0.3.0.dist-info → scruby-0.5.0.dist-info}/METADATA +31 -19
- scruby-0.5.0.dist-info/RECORD +8 -0
- scruby-0.3.0.dist-info/RECORD +0 -7
- {scruby-0.3.0.dist-info → scruby-0.5.0.dist-info}/WHEEL +0 -0
- {scruby-0.3.0.dist-info → scruby-0.5.0.dist-info}/licenses/LICENSE +0 -0
scruby/__init__.py
CHANGED
|
@@ -12,6 +12,10 @@ There is no need to iterate through all the keys in search of the desired value.
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
-
__all__ = (
|
|
15
|
+
__all__ = (
|
|
16
|
+
"Scruby",
|
|
17
|
+
"constants",
|
|
18
|
+
)
|
|
16
19
|
|
|
20
|
+
from scruby import constants
|
|
17
21
|
from scruby.db import Scruby
|
scruby/constants.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""Constant variables.
|
|
2
|
+
|
|
3
|
+
The module contains the following variables:
|
|
4
|
+
|
|
5
|
+
- `DB_ROOT` - Path to root directory of database. By default = "ScrubyDB" (in root of project).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
# Path to root directory of database. By default = "ScrubyDB" (in root of project).
|
|
11
|
+
DB_ROOT: str = "ScrubyDB"
|
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,28 @@ __all__ = ("Scruby",)
|
|
|
21
6
|
|
|
22
7
|
import hashlib
|
|
23
8
|
from shutil import rmtree
|
|
24
|
-
from typing import
|
|
9
|
+
from typing import TypeVar
|
|
25
10
|
|
|
26
11
|
import orjson
|
|
27
12
|
from anyio import Path, to_thread
|
|
28
13
|
|
|
29
|
-
|
|
14
|
+
from scruby import constants
|
|
30
15
|
|
|
16
|
+
T = TypeVar("T")
|
|
31
17
|
|
|
32
|
-
class Scruby:
|
|
33
|
-
"""Creation and management of the database.
|
|
34
18
|
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
19
|
+
class Scruby[T]:
|
|
20
|
+
"""Creation and management of the database.
|
|
48
21
|
|
|
49
22
|
Args:
|
|
50
|
-
|
|
23
|
+
class_model: Class of Model (Pydantic).
|
|
51
24
|
"""
|
|
52
25
|
|
|
53
26
|
def __init__( # noqa: D107
|
|
54
27
|
self,
|
|
55
|
-
|
|
28
|
+
class_model: T,
|
|
56
29
|
) -> None:
|
|
57
|
-
|
|
58
|
-
self.__db_path = db_path
|
|
59
|
-
|
|
60
|
-
@property
|
|
61
|
-
def db_path(self) -> str:
|
|
62
|
-
"""Get database name."""
|
|
63
|
-
return self.__db_path
|
|
30
|
+
self.__class_model = class_model
|
|
64
31
|
|
|
65
32
|
async def get_leaf_path(self, key: str) -> Path:
|
|
66
33
|
"""Get the path to the database cell by key.
|
|
@@ -71,10 +38,10 @@ class Scruby:
|
|
|
71
38
|
# Key to md5 sum.
|
|
72
39
|
key_md5: str = hashlib.md5(key.encode("utf-8")).hexdigest() # noqa: S324
|
|
73
40
|
# Convert md5 sum in the segment of path.
|
|
74
|
-
|
|
41
|
+
separated_md5: str = "/".join(list(key_md5))
|
|
75
42
|
# The path of the branch to the database.
|
|
76
43
|
branch_path: Path = Path(
|
|
77
|
-
*(self.
|
|
44
|
+
*(constants.DB_ROOT, self.__class_model.__name__, separated_md5),
|
|
78
45
|
)
|
|
79
46
|
# If the branch does not exist, need to create it.
|
|
80
47
|
if not await branch_path.exists():
|
|
@@ -86,46 +53,31 @@ class Scruby:
|
|
|
86
53
|
async def set_key(
|
|
87
54
|
self,
|
|
88
55
|
key: str,
|
|
89
|
-
value:
|
|
56
|
+
value: T,
|
|
90
57
|
) -> None:
|
|
91
58
|
"""Asynchronous method for adding and updating keys to database.
|
|
92
59
|
|
|
93
|
-
Examples:
|
|
94
|
-
>>> from scruby import Scruby
|
|
95
|
-
>>> db = Scruby()
|
|
96
|
-
>>> await db.set_key("key name", "Some text")
|
|
97
|
-
None
|
|
98
|
-
|
|
99
60
|
Args:
|
|
100
61
|
key: Key name.
|
|
101
62
|
value: Value of key.
|
|
102
63
|
"""
|
|
103
64
|
# The path to the database cell.
|
|
104
65
|
leaf_path: Path = await self.get_leaf_path(key)
|
|
66
|
+
value_json: str = value.model_dump_json()
|
|
105
67
|
# Write key-value to the database.
|
|
106
68
|
if await leaf_path.exists():
|
|
107
69
|
# Add new key or update existing.
|
|
108
70
|
data_json: bytes = await leaf_path.read_bytes()
|
|
109
71
|
data: dict = orjson.loads(data_json) or {}
|
|
110
|
-
data[key] =
|
|
72
|
+
data[key] = value_json
|
|
111
73
|
await leaf_path.write_bytes(orjson.dumps(data))
|
|
112
74
|
else:
|
|
113
75
|
# Add new key to a blank leaf.
|
|
114
|
-
await leaf_path.write_bytes(
|
|
76
|
+
await leaf_path.write_bytes(orjson.dumps({key: value_json}))
|
|
115
77
|
|
|
116
|
-
async def get_key(self, key: str) ->
|
|
78
|
+
async def get_key(self, key: str) -> T:
|
|
117
79
|
"""Asynchronous method for getting key from database.
|
|
118
80
|
|
|
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
81
|
Args:
|
|
130
82
|
key: Key name.
|
|
131
83
|
"""
|
|
@@ -135,22 +87,13 @@ class Scruby:
|
|
|
135
87
|
if await leaf_path.exists():
|
|
136
88
|
data_json: bytes = await leaf_path.read_bytes()
|
|
137
89
|
data: dict = orjson.loads(data_json) or {}
|
|
138
|
-
|
|
90
|
+
obj: T = self.__class_model.model_validate_json(data[key])
|
|
91
|
+
return obj
|
|
139
92
|
raise KeyError()
|
|
140
93
|
|
|
141
94
|
async def has_key(self, key: str) -> bool:
|
|
142
95
|
"""Asynchronous method for checking presence of key in database.
|
|
143
96
|
|
|
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
97
|
Args:
|
|
155
98
|
key: Key name.
|
|
156
99
|
"""
|
|
@@ -170,16 +113,6 @@ class Scruby:
|
|
|
170
113
|
async def delete_key(self, key: str) -> None:
|
|
171
114
|
"""Asynchronous method for deleting key from database.
|
|
172
115
|
|
|
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
116
|
Args:
|
|
184
117
|
key: Key name.
|
|
185
118
|
"""
|
|
@@ -195,20 +128,12 @@ class Scruby:
|
|
|
195
128
|
raise KeyError()
|
|
196
129
|
|
|
197
130
|
async def napalm(self) -> None:
|
|
198
|
-
"""Asynchronous method for full database deletion (Arg:
|
|
131
|
+
"""Asynchronous method for full database deletion (Arg: db_name).
|
|
132
|
+
|
|
133
|
+
The main purpose is tests.
|
|
199
134
|
|
|
200
135
|
Warning:
|
|
201
136
|
- `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
137
|
"""
|
|
213
|
-
await to_thread.run_sync(rmtree,
|
|
138
|
+
await to_thread.run_sync(rmtree, constants.DB_ROOT)
|
|
214
139
|
return
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scruby
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.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,36 +95,47 @@ uv add scruby
|
|
|
94
95
|
|
|
95
96
|
```python
|
|
96
97
|
import anyio
|
|
97
|
-
|
|
98
|
+
import datetime
|
|
99
|
+
from pydantic import BaseModel
|
|
100
|
+
from scruby import Scruby, constants
|
|
98
101
|
|
|
102
|
+
constants.DB_ROOT = "ScrubyDB" # By default = "ScrubyDB"
|
|
103
|
+
|
|
104
|
+
class User(BaseModel):
|
|
105
|
+
"""Model of User."""
|
|
106
|
+
|
|
107
|
+
first_name: str
|
|
108
|
+
last_name: str
|
|
109
|
+
birthday: datetime.datetime
|
|
110
|
+
email: str
|
|
111
|
+
phone: str
|
|
99
112
|
|
|
100
113
|
async def main() -> None:
|
|
101
114
|
"""Example."""
|
|
115
|
+
db = Scruby(User)
|
|
102
116
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
117
|
+
user = User(
|
|
118
|
+
first_name="John",
|
|
119
|
+
last_name="Smith",
|
|
120
|
+
birthday=datetime.datetime(1970, 1, 1),
|
|
121
|
+
email="John_Smith@gmail.com",
|
|
122
|
+
phone="+447986123456",
|
|
123
|
+
)
|
|
107
124
|
|
|
108
|
-
|
|
109
|
-
"first name": "John",
|
|
110
|
-
"last name": "Smith",
|
|
111
|
-
"email": "John_Smith@gmail.com",
|
|
112
|
-
"phone": "+447986123456",
|
|
113
|
-
}
|
|
114
|
-
await db.set_key("+447986123456", user_details)
|
|
125
|
+
await db.set_key("+447986123456", user)
|
|
115
126
|
|
|
116
|
-
await db.get_key("
|
|
127
|
+
await db.get_key("+447986123456") # => user
|
|
117
128
|
await db.get_key("key missing") # => KeyError
|
|
118
129
|
|
|
119
|
-
await db.has_key("
|
|
130
|
+
await db.has_key("+447986123456") # => True
|
|
120
131
|
await db.has_key("key missing") # => False
|
|
121
132
|
|
|
122
|
-
await db.delete_key("
|
|
123
|
-
await db.delete_key("
|
|
133
|
+
await db.delete_key("+447986123456")
|
|
134
|
+
await db.delete_key("+447986123456") # => KeyError
|
|
124
135
|
await db.delete_key("key missing") # => KeyError
|
|
125
136
|
|
|
126
137
|
# Full database deletion.
|
|
138
|
+
# Hint: The main purpose is tests.
|
|
127
139
|
await db.napalm()
|
|
128
140
|
await db.napalm() # => FileNotFoundError
|
|
129
141
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
scruby/__init__.py,sha256=LnHBN1pIOtT89baSQoknQwYI1cy-hmN1Lo0k8o1Ms48,659
|
|
2
|
+
scruby/constants.py,sha256=kwF0FIbeChBxsNxOCQhMsDEn1lakD7MIQKJ-PHYeSAo,328
|
|
3
|
+
scruby/db.py,sha256=iG1D4-ncVrVysp7OXH-eZksnNacjNna4_8nUbMkWnSE,4409
|
|
4
|
+
scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
scruby-0.5.0.dist-info/METADATA,sha256=6V1NV3KWNRsc2Gb2cZbBQ4VsLYbIDMWkCiM9CjkJyxo,6509
|
|
6
|
+
scruby-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
7
|
+
scruby-0.5.0.dist-info/licenses/LICENSE,sha256=2zZINd6m_jNYlowdQImlEizyhSui5cBAJZRhWQURcEc,1095
|
|
8
|
+
scruby-0.5.0.dist-info/RECORD,,
|
scruby-0.3.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.3.0.dist-info/METADATA,sha256=Dkz32lF7SmIWBEdXNhuhHQkYunAas6A84cEaVu08bYM,6256
|
|
5
|
-
scruby-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
6
|
-
scruby-0.3.0.dist-info/licenses/LICENSE,sha256=2zZINd6m_jNYlowdQImlEizyhSui5cBAJZRhWQURcEc,1095
|
|
7
|
-
scruby-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|