scruby 0.28.3__py3-none-any.whl → 0.30.2__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 CHANGED
@@ -28,7 +28,8 @@ from __future__ import annotations
28
28
  __all__ = (
29
29
  "settings",
30
30
  "Scruby",
31
+ "ScrubyModel",
31
32
  )
32
33
 
33
34
  from scruby import settings
34
- from scruby.db import Scruby
35
+ from scruby.db import Scruby, ScrubyModel
scruby/db.py CHANGED
@@ -6,12 +6,16 @@
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- __all__ = ("Scruby",)
9
+ __all__ = (
10
+ "Scruby",
11
+ "ScrubyModel",
12
+ )
10
13
 
11
14
  import contextlib
12
15
  import logging
13
16
  import re
14
17
  import zlib
18
+ from datetime import datetime
15
19
  from shutil import rmtree
16
20
  from typing import Any, Literal, Never, assert_never
17
21
 
@@ -31,6 +35,13 @@ class _Meta(BaseModel):
31
35
  counter_documents: int
32
36
 
33
37
 
38
+ class ScrubyModel(BaseModel):
39
+ """Additional fields for models."""
40
+
41
+ created_at: datetime | None = None
42
+ updated_at: datetime | None = None
43
+
44
+
34
45
  class Scruby(
35
46
  mixins.Keys,
36
47
  mixins.Find,
@@ -53,13 +64,13 @@ class Scruby(
53
64
  # The maximum number of branches.
54
65
  match self._hash_reduce_left:
55
66
  case 0:
56
- self._max_branch_number = 4294967296
67
+ self._max_number_branch = 4294967296
57
68
  case 2:
58
- self._max_branch_number = 16777216
69
+ self._max_number_branch = 16777216
59
70
  case 4:
60
- self._max_branch_number = 65536
71
+ self._max_number_branch = 65536
61
72
  case 6:
62
- self._max_branch_number = 256
73
+ self._max_number_branch = 256
63
74
  case _ as unreachable:
64
75
  msg: str = f"{unreachable} - Unacceptable value for HASH_REDUCE_LEFT."
65
76
  logging.critical(msg)
@@ -70,42 +81,67 @@ class Scruby(
70
81
  """Get an object to access a collection.
71
82
 
72
83
  Args:
73
- class_model: Class of Model (pydantic.BaseModel).
84
+ class_model (Any): Class of Model (ScrubyModel).
74
85
 
75
86
  Returns:
76
87
  Instance of Scruby for access a collection.
77
88
  """
78
- assert BaseModel in class_model.__bases__, "`class_model` does not contain the base class `pydantic.BaseModel`!"
79
-
89
+ if __debug__:
90
+ # Check if the object belongs to the class `ScrubyModel`
91
+ if ScrubyModel not in class_model.__bases__:
92
+ msg = (
93
+ "Method: `collection` => argument `class_model` " + "does not contain the base class `ScrubyModel`!"
94
+ )
95
+ raise AssertionError(msg)
96
+ # Checking the model for the presence of a key.
97
+ model_fields = list(class_model.model_fields.keys())
98
+ if "key" not in model_fields:
99
+ msg = f"Model: {class_model.__name__} => The `key` field is missing!"
100
+ raise AssertionError(msg)
101
+ if "created_at" not in model_fields:
102
+ msg = f"Model: {class_model.__name__} => The `created_at` field is missing!"
103
+ raise AssertionError(msg)
104
+ if "updated_at" not in model_fields:
105
+ msg = f"Model: {class_model.__name__} => The `updated_at` field is missing!"
106
+ raise AssertionError(msg)
107
+ # Check the length of the collection name for an acceptable size.
108
+ len_db_root_absolut_path = len(str(await Path(settings.DB_ROOT).resolve()).encode("utf-8"))
109
+ len_model_name = len(class_model.__name__)
110
+ len_full_path_leaf = len_db_root_absolut_path + len_model_name + 26
111
+ if len_full_path_leaf > 255:
112
+ excess = len_full_path_leaf - 255
113
+ msg = (
114
+ f"Model: {class_model.__name__} => The collection name is too long, "
115
+ + f"it exceeds the limit of {excess} characters!"
116
+ )
117
+ raise AssertionError(msg)
118
+ # Create instance of Scruby
80
119
  instance = cls()
120
+ # Add model class to Scruby
81
121
  instance.__dict__["_class_model"] = class_model
82
- # Caching a pati for metadata.
83
- # The zero branch is reserved for metadata.
84
- branch_number: int = 0
85
- branch_number_as_hash: str = f"{branch_number:08x}"[settings.HASH_REDUCE_LEFT :]
86
- separated_hash: str = "/".join(list(branch_number_as_hash))
122
+ # Create a path for metadata.
87
123
  meta_dir_path_tuple = (
88
124
  settings.DB_ROOT,
89
125
  class_model.__name__,
90
- separated_hash,
126
+ "meta",
91
127
  )
92
128
  instance.__dict__["_meta_path"] = Path(
93
129
  *meta_dir_path_tuple,
94
130
  "meta.json",
95
131
  )
96
132
  # Create metadata for collection, if missing.
97
- branch_path = Path(*meta_dir_path_tuple)
98
- if not await branch_path.exists():
99
- await branch_path.mkdir(parents=True)
133
+ meta_dir_path = Path(*meta_dir_path_tuple)
134
+ if not await meta_dir_path.exists():
135
+ await meta_dir_path.mkdir(parents=True)
100
136
  meta = _Meta(
101
137
  db_root=settings.DB_ROOT,
102
138
  collection_name=class_model.__name__,
103
139
  hash_reduce_left=settings.HASH_REDUCE_LEFT,
104
- max_branch_number=instance.__dict__["_max_branch_number"],
140
+ max_branch_number=instance.__dict__["_max_number_branch"],
105
141
  counter_documents=0,
106
142
  )
107
143
  meta_json = meta.model_dump_json()
108
- meta_path = Path(*(branch_path, "meta.json"))
144
+ meta_path = Path(*(meta_dir_path, "meta.json"))
109
145
  await meta_path.write_text(meta_json, "utf-8")
110
146
  return instance
111
147
 
scruby/mixins/count.py CHANGED
@@ -31,18 +31,19 @@ class Count:
31
31
  ) -> int:
32
32
  """Count the number of documents a matching the filter in this collection.
33
33
 
34
- The search is based on the effect of a quantum loop.
35
- The search effectiveness depends on the number of processor threads.
34
+ Attention:
35
+ - The search is based on the effect of a quantum loop.
36
+ - The search effectiveness depends on the number of processor threads.
36
37
 
37
38
  Args:
38
- filter_fn: A function that execute the conditions of filtering.
39
+ filter_fn (Callable): A function that execute the conditions of filtering.
39
40
 
40
41
  Returns:
41
42
  The number of documents.
42
43
  """
43
44
  # Variable initialization
44
45
  search_task_fn: Callable = self._task_find
45
- branch_numbers: range = range(1, self._max_branch_number)
46
+ branch_numbers: range = range(self._max_number_branch)
46
47
  hash_reduce_left: int = self._hash_reduce_left
47
48
  db_root: str = self._db_root
48
49
  class_model: Any = self._class_model
@@ -50,25 +50,25 @@ class CustomTask:
50
50
  docs.append(class_model.model_validate_json(val))
51
51
  return docs
52
52
 
53
- async def run_custom_task(self, custom_task_fn: Callable, limit_docs: int = 1000) -> Any:
53
+ async def run_custom_task(self, custom_task_fn: Callable) -> Any:
54
54
  """Running custom task.
55
55
 
56
- This method running a task created on the basis of a quantum loop.
57
- Effectiveness running task depends on the number of processor threads.
56
+ Attention:
57
+ - The search is based on the effect of a quantum loop.
58
+ - The search effectiveness depends on the number of processor threads.
58
59
 
59
60
  Args:
60
- custom_task_fn: A function that execute the custom task.
61
- limit_docs: Limiting the number of documents. By default = 1000.
61
+ custom_task_fn (Callable): A function that execute the custom task.
62
62
 
63
63
  Returns:
64
64
  The result of a custom task.
65
65
  """
66
66
  kwargs = {
67
67
  "get_docs_fn": self._task_get_docs,
68
- "branch_numbers": range(1, self._max_branch_number),
68
+ "branch_numbers": range(self._max_number_branch),
69
69
  "hash_reduce_left": self._hash_reduce_left,
70
70
  "db_root": self._db_root,
71
71
  "class_model": self._class_model,
72
- "limit_docs": limit_docs,
72
+ "max_workers": self._max_workers,
73
73
  }
74
74
  return await custom_task_fn(**kwargs)
scruby/mixins/delete.py CHANGED
@@ -64,18 +64,19 @@ class Delete:
64
64
  ) -> int:
65
65
  """Delete one or more documents matching the filter.
66
66
 
67
- The search is based on the effect of a quantum loop.
68
- The search effectiveness depends on the number of processor threads.
67
+ Attention:
68
+ - The search is based on the effect of a quantum loop.
69
+ - The search effectiveness depends on the number of processor threads.
69
70
 
70
71
  Args:
71
- filter_fn: A function that execute the conditions of filtering.
72
+ filter_fn (Callable): A function that execute the conditions of filtering.
72
73
 
73
74
  Returns:
74
75
  The number of deleted documents.
75
76
  """
76
77
  # Variable initialization
77
78
  search_task_fn: Callable = self._task_delete
78
- branch_numbers: range = range(1, self._max_branch_number)
79
+ branch_numbers: range = range(self._max_number_branch)
79
80
  hash_reduce_left: int = self._hash_reduce_left
80
81
  db_root: str = self._db_root
81
82
  class_model: Any = self._class_model
scruby/mixins/find.py CHANGED
@@ -61,8 +61,9 @@ class Find:
61
61
  ) -> Any | None:
62
62
  """Find one document matching the filter.
63
63
 
64
- The search is based on the effect of a quantum loop.
65
- The search effectiveness depends on the number of processor threads.
64
+ Attention:
65
+ - The search is based on the effect of a quantum loop.
66
+ - The search effectiveness depends on the number of processor threads.
66
67
 
67
68
  Args:
68
69
  filter_fn (Callable): A function that execute the conditions of filtering.
@@ -72,7 +73,7 @@ class Find:
72
73
  """
73
74
  # Variable initialization
74
75
  search_task_fn: Callable = self._task_find
75
- branch_numbers: range = range(1, self._max_branch_number)
76
+ branch_numbers: range = range(self._max_number_branch)
76
77
  hash_reduce_left: int = self._hash_reduce_left
77
78
  db_root: str = self._db_root
78
79
  class_model: Any = self._class_model
@@ -100,8 +101,9 @@ class Find:
100
101
  ) -> list[Any] | None:
101
102
  """Find many documents matching the filter.
102
103
 
103
- The search is based on the effect of a quantum loop.
104
- The search effectiveness depends on the number of processor threads.
104
+ Attention:
105
+ - The search is based on the effect of a quantum loop.
106
+ - The search effectiveness depends on the number of processor threads.
105
107
 
106
108
  Args:
107
109
  filter_fn (Callable): A function that execute the conditions of filtering.
@@ -117,7 +119,7 @@ class Find:
117
119
  assert page_number > 0, "`find_many` => The `page_number` parameter must not be less than one."
118
120
  # Variable initialization
119
121
  search_task_fn: Callable = self._task_find
120
- branch_numbers: range = range(1, self._max_branch_number)
122
+ branch_numbers: range = range(self._max_number_branch)
121
123
  hash_reduce_left: int = self._hash_reduce_left
122
124
  db_root: str = self._db_root
123
125
  class_model: Any = self._class_model
scruby/mixins/keys.py CHANGED
@@ -9,7 +9,9 @@ from __future__ import annotations
9
9
  __all__ = ("Keys",)
10
10
 
11
11
  import logging
12
+ from datetime import datetime
12
13
  from typing import Any
14
+ from zoneinfo import ZoneInfo
13
15
 
14
16
  import orjson
15
17
 
@@ -26,7 +28,7 @@ class Keys:
26
28
  """Asynchronous method for adding document to collection.
27
29
 
28
30
  Args:
29
- doc: Value of key. Type, derived from `BaseModel`.
31
+ doc (Any): Value of key. Type, derived from `ScrubyModel`.
30
32
 
31
33
  Returns:
32
34
  None.
@@ -42,6 +44,11 @@ class Keys:
42
44
  raise TypeError(msg)
43
45
  # The path to cell of collection.
44
46
  leaf_path, prepared_key = await self._get_leaf_path(doc.key)
47
+ # Init a `created_at` and `updated_at` fields
48
+ tz = ZoneInfo("UTC")
49
+ doc.created_at = datetime.now(tz)
50
+ doc.updated_at = datetime.now(tz)
51
+ # Convert doc to json
45
52
  doc_json: str = doc.model_dump_json()
46
53
  # Write key-value to collection.
47
54
  if await leaf_path.exists():
@@ -63,10 +70,10 @@ class Keys:
63
70
  await self._counter_documents(1)
64
71
 
65
72
  async def update_doc(self, doc: Any) -> None:
66
- """Asynchronous method for updating key to collection.
73
+ """Asynchronous method for updating document to collection.
67
74
 
68
75
  Args:
69
- doc: Value of key. Type `BaseModel`.
76
+ doc (Any): Value of key. Type `ScrubyModel`.
70
77
 
71
78
  Returns:
72
79
  None.
@@ -83,6 +90,9 @@ class Keys:
83
90
  raise TypeError(msg)
84
91
  # The path to cell of collection.
85
92
  leaf_path, prepared_key = await self._get_leaf_path(doc.key)
93
+ # Update a `updated_at` field
94
+ doc.updated_at = datetime.now(ZoneInfo("UTC"))
95
+ # Convert doc to json
86
96
  doc_json: str = doc.model_dump_json()
87
97
  # Update the existing key.
88
98
  if await leaf_path.exists():
@@ -98,14 +108,15 @@ class Keys:
98
108
  logging.error(err.message)
99
109
  raise err from None
100
110
  else:
101
- logging.error("The key not exists.")
102
- raise KeyError()
111
+ msg: str = f"`update_doc` - The key `{doc.key}` is missing!"
112
+ logging.error(msg)
113
+ raise KeyError(msg)
103
114
 
104
- async def get_key(self, key: str) -> Any:
105
- """Asynchronous method for getting value of key from collection.
115
+ async def get_doc(self, key: str) -> Any:
116
+ """Asynchronous method for getting document from collection the by key.
106
117
 
107
118
  Args:
108
- key: Key name.
119
+ key (str): Key name.
109
120
 
110
121
  Returns:
111
122
  Value of key or KeyError.
@@ -118,15 +129,15 @@ class Keys:
118
129
  data: dict = orjson.loads(data_json) or {}
119
130
  obj: Any = self._class_model.model_validate_json(data[prepared_key])
120
131
  return obj
121
- msg: str = "`get_key` - The unacceptable key value."
132
+ msg: str = f"`get_doc` - The key `{key}` is missing!"
122
133
  logging.error(msg)
123
- raise KeyError()
134
+ raise KeyError(msg)
124
135
 
125
136
  async def has_key(self, key: str) -> bool:
126
137
  """Asynchronous method for checking presence of key in collection.
127
138
 
128
139
  Args:
129
- key: Key name.
140
+ key (str): Key name.
130
141
 
131
142
  Returns:
132
143
  True, if the key is present.
@@ -144,11 +155,11 @@ class Keys:
144
155
  return False
145
156
  return False
146
157
 
147
- async def delete_key(self, key: str) -> None:
148
- """Asynchronous method for deleting key from collection.
158
+ async def delete_doc(self, key: str) -> None:
159
+ """Asynchronous method for deleting document from collection the by key.
149
160
 
150
161
  Args:
151
- key: Key name.
162
+ key (str): Key name.
152
163
 
153
164
  Returns:
154
165
  None.
@@ -163,6 +174,6 @@ class Keys:
163
174
  await leaf_path.write_bytes(orjson.dumps(data))
164
175
  await self._counter_documents(-1)
165
176
  return
166
- msg: str = "`delete_key` - The unacceptable key value."
177
+ msg: str = f"`delete_doc` - The key `{key}` is missing!"
167
178
  logging.error(msg)
168
- raise KeyError()
179
+ raise KeyError(msg)
scruby/mixins/update.py CHANGED
@@ -62,24 +62,27 @@ class Update:
62
62
 
63
63
  async def update_many(
64
64
  self,
65
- filter_fn: Callable,
66
65
  new_data: dict[str, Any],
66
+ filter_fn: Callable = lambda _: True,
67
67
  ) -> int:
68
- """Updates one or more documents matching the filter.
68
+ """Updates many documents matching the filter.
69
69
 
70
- The search is based on the effect of a quantum loop.
71
- The search effectiveness depends on the number of processor threads.
70
+ Attention:
71
+ - For a complex case, a custom task may be needed.
72
+ - See documentation on creating custom tasks.
73
+ - The search is based on the effect of a quantum loop.
74
+ - The search effectiveness depends on the number of processor threads.
72
75
 
73
76
  Args:
74
- filter_fn: A function that execute the conditions of filtering.
75
- new_data: New data for the fields that need to be updated.
77
+ filter_fn (Callable): A function that execute the conditions of filtering.
78
+ new_data (dict[str, Any]): New data for the fields that need to be updated.
76
79
 
77
80
  Returns:
78
81
  The number of updated documents.
79
82
  """
80
83
  # Variable initialization
81
84
  update_task_fn: Callable = self._task_update
82
- branch_numbers: range = range(1, self._max_branch_number)
85
+ branch_numbers: range = range(self._max_number_branch)
83
86
  hash_reduce_left: int = self._hash_reduce_left
84
87
  db_root: str = self._db_root
85
88
  class_model: Any = self._class_model
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scruby
3
- Version: 0.28.3
3
+ Version: 0.30.2
4
4
  Summary: Asynchronous library for building and managing a hybrid database, by scheme of key-value.
5
5
  Project-URL: Homepage, https://kebasyaty.github.io/scruby/
6
6
  Project-URL: Repository, https://github.com/kebasyaty/scruby
@@ -99,6 +99,15 @@ Online browsable documentation is available at [https://kebasyaty.github.io/scru
99
99
  uv add scruby
100
100
  ```
101
101
 
102
+ ## Run
103
+
104
+ ```shell
105
+ # Run Development:
106
+ uv run python main.py
107
+ # Run Production:
108
+ uv run python -OOP main.py
109
+ ```
110
+
102
111
  ## Usage
103
112
 
104
113
  See more examples here [https://kebasyaty.github.io/scruby/latest/pages/usage/](https://kebasyaty.github.io/scruby/latest/pages/usage/ "Examples").
@@ -107,24 +116,25 @@ See more examples here [https://kebasyaty.github.io/scruby/latest/pages/usage/](
107
116
  """Working with keys."""
108
117
 
109
118
  import anyio
110
- import datetime
119
+ from datetime import datetime
120
+ from zoneinfo import ZoneInfo
111
121
  from typing import Annotated
112
- from pydantic import BaseModel, EmailStr, Field
122
+ from pydantic import EmailStr, Field
113
123
  from pydantic_extra_types.phone_numbers import PhoneNumber, PhoneNumberValidator
114
- from scruby import Scruby, settings
124
+ from scruby import Scruby, ScrubyModel, settings
115
125
 
116
126
  settings.DB_ROOT = "ScrubyDB" # By default = "ScrubyDB"
117
127
  settings.HASH_REDUCE_LEFT = 6 # By default = 6
118
128
  settings.MAX_WORKERS = None # By default = None
119
129
 
120
- class User(BaseModel):
130
+ class User(ScrubyModel):
121
131
  """User model."""
122
132
  first_name: str = Field(strict=True)
123
133
  last_name: str = Field(strict=True)
124
- birthday: datetime.datetime = Field(strict=True)
134
+ birthday: datetime = Field(strict=True)
125
135
  email: EmailStr = Field(strict=True)
126
136
  phone: Annotated[PhoneNumber, PhoneNumberValidator(number_format="E164")] = Field(frozen=True)
127
- # The key is always at the bottom
137
+ # key is always at bottom
128
138
  key: str = Field(
129
139
  strict=True,
130
140
  frozen=True,
@@ -140,7 +150,7 @@ async def main() -> None:
140
150
  user = User(
141
151
  first_name="John",
142
152
  last_name="Smith",
143
- birthday=datetime.datetime(1970, 1, 1),
153
+ birthday=datetime.datetime(1970, 1, 1, tzinfo=ZoneInfo("UTC")),
144
154
  email="John_Smith@gmail.com",
145
155
  phone="+447986123456",
146
156
  )
@@ -149,15 +159,15 @@ async def main() -> None:
149
159
 
150
160
  await user_coll.update_doc(user)
151
161
 
152
- await user_coll.get_key("+447986123456") # => user
153
- await user_coll.get_key("key missing") # => KeyError
162
+ await user_coll.get_doc("+447986123456") # => user
163
+ await user_coll.get_doc("key missing") # => KeyError
154
164
 
155
165
  await user_coll.has_key("+447986123456") # => True
156
166
  await user_coll.has_key("key missing") # => False
157
167
 
158
- await user_coll.delete_key("+447986123456")
159
- await user_coll.delete_key("+447986123456") # => KeyError
160
- await user_coll.delete_key("key missing") # => KeyError
168
+ await user_coll.delete_doc("+447986123456")
169
+ await user_coll.delete_doc("+447986123456") # => KeyError
170
+ await user_coll.delete_doc("key missing") # => KeyError
161
171
 
162
172
  # Full database deletion.
163
173
  # Hint: The main purpose is tests.
@@ -176,10 +186,10 @@ The search effectiveness depends on the number of processor threads.
176
186
  """
177
187
 
178
188
  import anyio
179
- import datetime
189
+ from datetime import datetime
180
190
  from typing import Annotated
181
- from pydantic import BaseModel, Field
182
- from scruby import Scruby, settings
191
+ from pydantic import Field
192
+ from scruby import Scruby, ScrubyModel, settings
183
193
  from pprint import pprint as pp
184
194
 
185
195
  settings.DB_ROOT = "ScrubyDB" # By default = "ScrubyDB"
@@ -187,13 +197,13 @@ settings.HASH_REDUCE_LEFT = 6 # By default = 6
187
197
  settings.MAX_WORKERS = None # By default = None
188
198
 
189
199
 
190
- class Phone(BaseModel):
200
+ class Phone(ScrubyModel):
191
201
  """Phone model."""
192
202
  brand: str = Field(strict=True, frozen=True)
193
203
  model: str = Field(strict=True, frozen=True)
194
204
  screen_diagonal: float = Field(strict=True)
195
205
  matrix_type: str = Field(strict=True)
196
- # The key is always at the bottom
206
+ # key is always at bottom
197
207
  key: str = Field(
198
208
  strict=True,
199
209
  frozen=True,
@@ -252,10 +262,10 @@ The search effectiveness depends on the number of processor threads.
252
262
  """
253
263
 
254
264
  import anyio
255
- import datetime
265
+ from datetime import datetime
256
266
  from typing import Annotated
257
- from pydantic import BaseModel, Field
258
- from scruby import Scruby, settings
267
+ from pydantic import Field
268
+ from scruby import Scruby, ScrubyModel, settings
259
269
  from pprint import pprint as pp
260
270
 
261
271
  settings.DB_ROOT = "ScrubyDB" # By default = "ScrubyDB"
@@ -263,13 +273,13 @@ settings.HASH_REDUCE_LEFT = 6 # By default = 6
263
273
  settings.MAX_WORKERS = None # By default = None
264
274
 
265
275
 
266
- class Car(BaseModel):
276
+ class Car(ScrubyModel):
267
277
  """Car model."""
268
278
  brand: str = Field(strict=True, frozen=True)
269
279
  model: str = Field(strict=True, frozen=True)
270
280
  year: int = Field(strict=True)
271
281
  power_reserve: int = Field(strict=True)
272
- # The key is always at the bottom
282
+ # key is always at bottom
273
283
  key: str = Field(
274
284
  strict=True,
275
285
  frozen=True,
@@ -0,0 +1,18 @@
1
+ scruby/__init__.py,sha256=WuIA27d3uUK0Vo0fXg92hge7i6v_2DBndhfepK16_y8,1046
2
+ scruby/aggregation.py,sha256=bd70J1Xye6faNHD8LS3lVQoHWKtPdPV_cqT_i7oui38,3491
3
+ scruby/db.py,sha256=4bSPMh0fYil0j9qhHhRu1g4U_0WaCuaGPthnmi2QpBI,8376
4
+ scruby/errors.py,sha256=D0jisudUsZk9iXp4nRSymaSMwyqHPVshsSlxx4HDVVk,1297
5
+ scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ scruby/settings.py,sha256=_uVdZIGWoi6q9zcu0c2PS51OBEBNASRRrxfzaF7Nwy0,1580
7
+ scruby/mixins/__init__.py,sha256=XPMjJvOZN7dLpTE1FfGMBGQ_0421HXug-0rWKMU5fRQ,627
8
+ scruby/mixins/collection.py,sha256=coF-IOhicV_EihDwnYf6SW5Mfi3nOFR0gAhCc619NmI,1382
9
+ scruby/mixins/count.py,sha256=YHjonxAtroSc5AlbDX1vpCvbe3vsTD0LfYnjEDB001A,2089
10
+ scruby/mixins/custom_task.py,sha256=ENr3FkCsPWRblOWv8jGMkkGKw4hvp9mMP2YjQvIeqzE,2246
11
+ scruby/mixins/delete.py,sha256=b7RYiTLUqVu70ep15CN1VTyKs13P7f-1YDGRc3ke-2g,3085
12
+ scruby/mixins/find.py,sha256=-rpILkxhfywJ5E3ceYo9dSPabma47ouceGajUVNh23Q,5396
13
+ scruby/mixins/keys.py,sha256=waxye5n0-oTWIhdDXGQkUTGaV9vLSCc9lCryTCuexkw,6179
14
+ scruby/mixins/update.py,sha256=WeNk2qZToQS3-r1_ahazBc2ErLMU9K5RRNgCxbu3JMg,3416
15
+ scruby-0.30.2.dist-info/METADATA,sha256=OgV4Foj3-X97NKFLpw_8Qb_osGwmYIINO2UZkgdLaY0,11032
16
+ scruby-0.30.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
17
+ scruby-0.30.2.dist-info/licenses/LICENSE,sha256=mS0Wz0yGNB63gEcWEnuIb_lldDYV0sjRaO-o_GL6CWE,1074
18
+ scruby-0.30.2.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- scruby/__init__.py,sha256=bw2Le5ULYlf2nFQT_617rmEumu66Ll-QCLCxqDFERWw,1014
2
- scruby/aggregation.py,sha256=bd70J1Xye6faNHD8LS3lVQoHWKtPdPV_cqT_i7oui38,3491
3
- scruby/db.py,sha256=djo4JkfuKCcV3jRbd2L3mIwENS3ptqJBt7SlAuiRhGY,6794
4
- scruby/errors.py,sha256=D0jisudUsZk9iXp4nRSymaSMwyqHPVshsSlxx4HDVVk,1297
5
- scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- scruby/settings.py,sha256=_uVdZIGWoi6q9zcu0c2PS51OBEBNASRRrxfzaF7Nwy0,1580
7
- scruby/mixins/__init__.py,sha256=XPMjJvOZN7dLpTE1FfGMBGQ_0421HXug-0rWKMU5fRQ,627
8
- scruby/mixins/collection.py,sha256=coF-IOhicV_EihDwnYf6SW5Mfi3nOFR0gAhCc619NmI,1382
9
- scruby/mixins/count.py,sha256=PGzRtgLvseQnHg6wt-A81s30pnsdY1d8cr-EQRnbfyU,2050
10
- scruby/mixins/custom_task.py,sha256=ZhvCDiYnJ8BTIWlnRu6cTH-9G9o7dHSixjMIsxAtDpw,2316
11
- scruby/mixins/delete.py,sha256=B2loiowj8ToO0euumDRxpHUVrLQx0iTcRys0jszn-rA,3046
12
- scruby/mixins/find.py,sha256=gnHjnm0MZbzMHmWOBUJbMm8LZFBqdJ6yWA6Pxfap51Q,5340
13
- scruby/mixins/keys.py,sha256=OUByWbHfNVWJVkUrhCsJZdVqf0zez_an6Gti2n5iKnM,5671
14
- scruby/mixins/update.py,sha256=YkUiz1gcVtNXdgf7Mmda-0g3vJm3jL09v-msGy2tAWg,3229
15
- scruby-0.28.3.dist-info/METADATA,sha256=DoDzTEj_wrZu3EfAhBcKBtCfbr9pb74FS3spyDpFfzY,10849
16
- scruby-0.28.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
17
- scruby-0.28.3.dist-info/licenses/LICENSE,sha256=mS0Wz0yGNB63gEcWEnuIb_lldDYV0sjRaO-o_GL6CWE,1074
18
- scruby-0.28.3.dist-info/RECORD,,