scruby 0.15.0__py3-none-any.whl → 0.16.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/aggregation.py CHANGED
@@ -4,37 +4,53 @@ from __future__ import annotations
4
4
 
5
5
  __all__ = (
6
6
  "Average",
7
+ "Counter",
7
8
  "Max",
8
9
  "Min",
9
10
  "Sum",
10
11
  )
11
12
 
13
+ from decimal import ROUND_HALF_EVEN, Decimal
12
14
  from typing import Any
13
15
 
14
16
 
15
17
  class Average:
16
- """Aggregation class for calculating the average value."""
18
+ """Aggregation class for calculating the average value.
17
19
 
18
- def __init__(self) -> None: # noqa: D107
19
- self.value = 0.0
20
- self.counter = 0.0
20
+ Args:
21
+ precision: The accuracy of rounding. `By default = .00`
22
+ rounding: Rounding mode. `By default = ROUND_HALF_EVEN`
23
+ """
24
+
25
+ def __init__( # noqa: D107
26
+ self,
27
+ precision: str = ".00",
28
+ rounding: str = ROUND_HALF_EVEN,
29
+ ) -> None:
30
+ self.value = Decimal()
31
+ self.counter = 0
32
+ self.precision = precision
33
+ self.rounding = rounding
21
34
 
22
35
  def set(self, number: int | float) -> None:
23
36
  """Add value.
24
37
 
25
38
  Args:
26
- number: Current value.
39
+ number: Current value (int | float).
27
40
  """
28
- self.value += float(number)
29
- self.counter += 1.0
41
+ self.value += Decimal(str(number))
42
+ self.counter += 1
30
43
 
31
- def get(self) -> float:
44
+ def get(self) -> Decimal:
32
45
  """Get arithmetic average value.
33
46
 
34
47
  Returns:
35
- Number (int|float) - Average value.
48
+ Number (Decimal) - Average value.
36
49
  """
37
- return self.value / self.counter
50
+ return (self.value / Decimal(str(self.counter))).quantize(
51
+ exp=Decimal(self.precision),
52
+ rounding=self.rounding,
53
+ )
38
54
 
39
55
 
40
56
  class Counter:
@@ -44,7 +60,7 @@ class Counter:
44
60
  limit: The maximum counter value.
45
61
  """
46
62
 
47
- def __init__(self, limit: int = 1000) -> None:
63
+ def __init__(self, limit: int = 1000) -> None: # noqa: D107
48
64
  self.limit = limit
49
65
  self.counter = 0
50
66
 
@@ -113,7 +129,7 @@ class Sum:
113
129
  """Aggregation class for calculating sum of values."""
114
130
 
115
131
  def __init__(self) -> None: # noqa: D107
116
- self.value: Any = 0
132
+ self.value = Decimal()
117
133
 
118
134
  def set(self, number: int | float) -> None:
119
135
  """Add value.
@@ -121,9 +137,9 @@ class Sum:
121
137
  Args:
122
138
  number: Current value.
123
139
  """
124
- self.value += number
140
+ self.value += Decimal(str(number))
125
141
 
126
- def get(self) -> Any:
142
+ def get(self) -> Decimal:
127
143
  """Get sum of values.
128
144
 
129
145
  Returns:
scruby/db.py CHANGED
@@ -18,6 +18,10 @@ from anyio import Path, to_thread
18
18
  from pydantic import BaseModel
19
19
 
20
20
  from scruby import constants
21
+ from scruby.errors import (
22
+ KeyAlreadyExistsError,
23
+ KeyNotExistsError,
24
+ )
21
25
 
22
26
  logger = logging.getLogger(__name__)
23
27
 
@@ -175,36 +179,75 @@ class Scruby[T]:
175
179
  leaf_path: Path = Path(*(branch_path, "leaf.json"))
176
180
  return leaf_path
177
181
 
178
- async def set_key(
182
+ async def add_key(
179
183
  self,
180
184
  key: str,
181
185
  value: T,
182
186
  ) -> None:
183
- """Asynchronous method for adding and updating keys to collection.
187
+ """Asynchronous method for adding key to collection.
184
188
 
185
189
  Args:
186
- key: Key name.
187
- value: Value of key.
190
+ key: Key name. Type `str`.
191
+ value: Value of key. Type `BaseModel`.
188
192
 
189
193
  Returns:
190
194
  None.
191
195
  """
192
- # The path to the database cell.
196
+ # The path to cell of collection.
193
197
  leaf_path: Path = await self._get_leaf_path(key)
194
198
  value_json: str = value.model_dump_json()
195
- # Write key-value to the database.
199
+ # Write key-value to collection.
196
200
  if await leaf_path.exists():
197
- # Add new key or update existing.
201
+ # Add new key.
198
202
  data_json: bytes = await leaf_path.read_bytes()
199
203
  data: dict = orjson.loads(data_json) or {}
200
- if data.get(key) is None:
201
- await self._counter_documents(1)
202
- data[key] = value_json
203
- await leaf_path.write_bytes(orjson.dumps(data))
204
+ try:
205
+ data[key]
206
+ except KeyError:
207
+ data[key] = value_json
208
+ await leaf_path.write_bytes(orjson.dumps(data))
209
+ else:
210
+ err = KeyAlreadyExistsError()
211
+ logger.error(err.message)
212
+ raise err
204
213
  else:
205
214
  # Add new key to a blank leaf.
206
215
  await leaf_path.write_bytes(orjson.dumps({key: value_json}))
207
- await self._counter_documents(1)
216
+ await self._counter_documents(1)
217
+
218
+ async def update_key(
219
+ self,
220
+ key: str,
221
+ value: T,
222
+ ) -> None:
223
+ """Asynchronous method for updating key to collection.
224
+
225
+ Args:
226
+ key: Key name. Type `str`.
227
+ value: Value of key. Type `BaseModel`.
228
+
229
+ Returns:
230
+ None.
231
+ """
232
+ # The path to cell of collection.
233
+ leaf_path: Path = await self._get_leaf_path(key)
234
+ value_json: str = value.model_dump_json()
235
+ # Update the existing key.
236
+ if await leaf_path.exists():
237
+ # Update the existing key.
238
+ data_json: bytes = await leaf_path.read_bytes()
239
+ data: dict = orjson.loads(data_json) or {}
240
+ try:
241
+ data[key]
242
+ data[key] = value_json
243
+ await leaf_path.write_bytes(orjson.dumps(data))
244
+ except KeyError:
245
+ err = KeyNotExistsError()
246
+ logger.error(err.message)
247
+ raise err from None
248
+ else:
249
+ logger.error("The key not exists.")
250
+ raise KeyError()
208
251
 
209
252
  async def get_key(self, key: str) -> T:
210
253
  """Asynchronous method for getting value of key from collection.
@@ -236,7 +279,7 @@ class Scruby[T]:
236
279
  Returns:
237
280
  True, if the key is present.
238
281
  """
239
- # The path to the database cell.
282
+ # Get path to cell of collection.
240
283
  leaf_path: Path = await self._get_leaf_path(key)
241
284
  # Checking whether there is a key.
242
285
  if await leaf_path.exists():
scruby/errors.py CHANGED
@@ -21,3 +21,19 @@ class MetadataValueError(ScrubyException):
21
21
  def __init__(self, message: str) -> None: # noqa: D107
22
22
  self.message = message
23
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.15.0
3
+ Version: 0.16.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
@@ -143,7 +143,9 @@ async def main() -> None:
143
143
  phone="+447986123456",
144
144
  )
145
145
 
146
- await user_coll.set_key("+447986123456", user)
146
+ await user_coll.add_key(user.phone, user)
147
+
148
+ await user_coll.update_key(user.phone, user)
147
149
 
148
150
  await user_coll.get_key("+447986123456") # => user
149
151
  await user_coll.get_key("key missing") # => KeyError
@@ -209,7 +211,7 @@ async def main() -> None:
209
211
  )
210
212
 
211
213
  # Add user to collection.
212
- await user_coll.set_key("+447986123456", user)
214
+ await user_coll.add_key(user.phone, user)
213
215
 
214
216
  # Find user by email.
215
217
  user_details: User | None = user_coll.find_one(
@@ -282,7 +284,7 @@ async def main() -> None:
282
284
  email=f"John_Smith_{num}@gmail.com",
283
285
  phone=f"+44798612345{num}",
284
286
  )
285
- await db.set_key(f"+44798612345{num}", user)
287
+ await user_coll.add_key(user.phone, user)
286
288
 
287
289
  # Find users by email.
288
290
  users: list[User] | None = user_coll.find_many(
@@ -0,0 +1,10 @@
1
+ scruby/__init__.py,sha256=GOVcjXmcOEDBbJQJDJlQq-x3M-VGJaMSN278EXsl2po,884
2
+ scruby/aggregation.py,sha256=_SUo9gL9yrtr94F-BNaBpuMkEfxtxtcvuOUqw7Ryygs,3466
3
+ scruby/constants.py,sha256=3LZfcxcuRqwzoB0-iogLMjKBZRdxfWJmTbyPwVRhQgY,1007
4
+ scruby/db.py,sha256=Oanc8M5JR33ffMS_MF5QebMZ8npQODeDgIttvFNcK8w,27079
5
+ scruby/errors.py,sha256=enLprEqfj_B2HcuOiU2oObZ5wUKyjY1VhGifXz84wew,1081
6
+ scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ scruby-0.16.0.dist-info/METADATA,sha256=xDv8w7suhu6nLuWzWz--4OgMK3mOKXd_CtmKdtTWkq0,10962
8
+ scruby-0.16.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
+ scruby-0.16.0.dist-info/licenses/LICENSE,sha256=2zZINd6m_jNYlowdQImlEizyhSui5cBAJZRhWQURcEc,1095
10
+ scruby-0.16.0.dist-info/RECORD,,
@@ -1,10 +0,0 @@
1
- scruby/__init__.py,sha256=GOVcjXmcOEDBbJQJDJlQq-x3M-VGJaMSN278EXsl2po,884
2
- scruby/aggregation.py,sha256=x_9ZJQHJHDISxRvddS5A2Hb0saIcfPTh1Veyf2KgX8A,2919
3
- scruby/constants.py,sha256=3LZfcxcuRqwzoB0-iogLMjKBZRdxfWJmTbyPwVRhQgY,1007
4
- scruby/db.py,sha256=FNW_o2JDd_RnGpOdsEfSubMH8kcO7CSoej52y9vJwnc,25769
5
- scruby/errors.py,sha256=aHQri4LNcFVQrSHwjyzb1fL8O49SwjYEU4QgMOo4uyA,622
6
- scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
- scruby-0.15.0.dist-info/METADATA,sha256=0-3PkQkRh7wCbeXnYaqvmE_BPeiKx3CrWT2VpC_CBPc,10925
8
- scruby-0.15.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
9
- scruby-0.15.0.dist-info/licenses/LICENSE,sha256=2zZINd6m_jNYlowdQImlEizyhSui5cBAJZRhWQURcEc,1095
10
- scruby-0.15.0.dist-info/RECORD,,