scruby 2.2.2__py3-none-any.whl → 2.3.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 +2 -1
- scruby/cache.py +28 -15
- scruby/db.py +20 -97
- scruby/meta.py +57 -0
- scruby/mixins/collection.py +15 -12
- scruby/model.py +17 -0
- {scruby-2.2.2.dist-info → scruby-2.3.0.dist-info}/METADATA +7 -7
- {scruby-2.2.2.dist-info → scruby-2.3.0.dist-info}/RECORD +11 -9
- {scruby-2.2.2.dist-info → scruby-2.3.0.dist-info}/WHEEL +0 -0
- {scruby-2.2.2.dist-info → scruby-2.3.0.dist-info}/licenses/GPL-3.0-LICENSE +0 -0
- {scruby-2.2.2.dist-info → scruby-2.3.0.dist-info}/licenses/MIT-LICENSE +0 -0
scruby/__init__.py
CHANGED
scruby/cache.py
CHANGED
|
@@ -12,11 +12,19 @@ from typing import Any, ClassVar, Literal, Never, assert_never, final
|
|
|
12
12
|
import orjson
|
|
13
13
|
|
|
14
14
|
from scruby.config import ScrubyConfig
|
|
15
|
+
from scruby.meta import Metadata
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
@final
|
|
18
19
|
class DocCache:
|
|
19
|
-
"""Cache documents to optimize work with the database.
|
|
20
|
+
"""Cache documents to optimize work with the database.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
collection_name (str): Collection name.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
None.
|
|
27
|
+
"""
|
|
20
28
|
|
|
21
29
|
# Cache structure:
|
|
22
30
|
# {"CollectionName": {"hash_symbol": {"hash_symbol": {"hash_symbol": {"key_name": doc}}}}
|
|
@@ -24,7 +32,14 @@ class DocCache:
|
|
|
24
32
|
|
|
25
33
|
@classmethod
|
|
26
34
|
def create_structure(cls, collection_name: str) -> None:
|
|
27
|
-
"""Create
|
|
35
|
+
"""Create structure of empty cache for collection.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
collection_name (str): Collection name.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
None.
|
|
42
|
+
"""
|
|
28
43
|
if ScrubyConfig.HASH_REDUCE_LEFT == 0:
|
|
29
44
|
return
|
|
30
45
|
|
|
@@ -38,30 +53,30 @@ class DocCache:
|
|
|
38
53
|
cls.cache[collection_name] = {
|
|
39
54
|
key: {key: {key: {} for key in hexdigits} for key in hexdigits} for key in hexdigits
|
|
40
55
|
}
|
|
41
|
-
case 0:
|
|
42
|
-
pass
|
|
43
56
|
case _ as unreachable:
|
|
44
57
|
assert_never(Never(unreachable)) # pyrefly: ignore[not-callable]
|
|
45
58
|
|
|
46
59
|
@classmethod
|
|
47
60
|
def load_cache(cls, subclasses: list[Any]) -> None:
|
|
48
61
|
"""Load all documents from the database into the cache."""
|
|
49
|
-
if ScrubyConfig.HASH_REDUCE_LEFT == 0:
|
|
50
|
-
return
|
|
51
|
-
|
|
52
62
|
db_root: Path = Path(ScrubyConfig.db_root)
|
|
53
63
|
HASH_REDUCE_LEFT: Literal[7, 6, 5, 0] = ScrubyConfig.HASH_REDUCE_LEFT
|
|
54
64
|
MAX_NUMBER_BRANCH: Literal[16, 256, 4096, 4294967296] = ScrubyConfig.MAX_NUMBER_BRANCH
|
|
55
65
|
branch_numbers: range = range(MAX_NUMBER_BRANCH)
|
|
56
66
|
|
|
57
|
-
# Leave function if database does not exist
|
|
58
|
-
if not db_root.exists():
|
|
59
|
-
return
|
|
60
|
-
|
|
61
67
|
for subclass in subclasses:
|
|
62
|
-
collection_name = subclass.__name__
|
|
68
|
+
collection_name: str = subclass.__name__
|
|
69
|
+
|
|
70
|
+
# Create metadata for collection
|
|
71
|
+
Metadata.create(collection_name)
|
|
72
|
+
|
|
73
|
+
if HASH_REDUCE_LEFT == 0:
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
# Create a cache structure for the collection
|
|
63
77
|
cls.create_structure(collection_name)
|
|
64
78
|
|
|
79
|
+
# Get data from database and add to cache for collection
|
|
65
80
|
for branch_number in branch_numbers:
|
|
66
81
|
branch_number_as_hash: str = f"{branch_number:08x}"[HASH_REDUCE_LEFT:]
|
|
67
82
|
separated_hash = "/".join(list(branch_number_as_hash))
|
|
@@ -79,7 +94,7 @@ class DocCache:
|
|
|
79
94
|
data: dict[str, str] = orjson.loads(data_json) or {}
|
|
80
95
|
for key, val in data.items():
|
|
81
96
|
doc = subclass.model_validate_json(val)
|
|
82
|
-
match
|
|
97
|
+
match HASH_REDUCE_LEFT:
|
|
83
98
|
case 7:
|
|
84
99
|
cls.cache[collection_name][branch_number_as_hash[0]][key] = doc
|
|
85
100
|
case 6:
|
|
@@ -90,7 +105,5 @@ class DocCache:
|
|
|
90
105
|
cls.cache[collection_name][branch_number_as_hash[0]][branch_number_as_hash[1]][
|
|
91
106
|
branch_number_as_hash[2]
|
|
92
107
|
][key] = doc
|
|
93
|
-
case 0:
|
|
94
|
-
pass
|
|
95
108
|
case _ as unreachable:
|
|
96
109
|
assert_never(Never(unreachable)) # pyrefly: ignore[not-callable]
|
scruby/db.py
CHANGED
|
@@ -6,41 +6,22 @@
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
-
__all__ = (
|
|
10
|
-
"Scruby",
|
|
11
|
-
"ScrubyModel",
|
|
12
|
-
)
|
|
9
|
+
__all__ = ("Scruby",)
|
|
13
10
|
|
|
14
11
|
import contextlib
|
|
15
12
|
import re
|
|
16
13
|
import zlib
|
|
17
|
-
from datetime import datetime
|
|
18
14
|
from shutil import rmtree
|
|
19
15
|
from typing import Any, Literal, final
|
|
20
16
|
|
|
21
17
|
from anyio import Path
|
|
22
|
-
from pydantic import BaseModel
|
|
23
18
|
from xloft import NamedTuple
|
|
24
19
|
|
|
25
20
|
from scruby import mixins
|
|
26
21
|
from scruby.cache import DocCache
|
|
27
22
|
from scruby.config import ScrubyConfig
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
class _Meta(BaseModel):
|
|
31
|
-
"""Metadata of Collection."""
|
|
32
|
-
|
|
33
|
-
collection_name: str
|
|
34
|
-
hash_reduce_left: int
|
|
35
|
-
max_number_branch: int
|
|
36
|
-
counter_documents: int
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class ScrubyModel(BaseModel):
|
|
40
|
-
"""Additional fields for models."""
|
|
41
|
-
|
|
42
|
-
created_at: datetime | None = None
|
|
43
|
-
updated_at: datetime | None = None
|
|
23
|
+
from scruby.meta import Meta
|
|
24
|
+
from scruby.model import ScrubyModel
|
|
44
25
|
|
|
45
26
|
|
|
46
27
|
@final
|
|
@@ -57,97 +38,39 @@ class Scruby(
|
|
|
57
38
|
|
|
58
39
|
def __init__( # noqa: D107
|
|
59
40
|
self,
|
|
41
|
+
class_model: Any,
|
|
60
42
|
) -> None:
|
|
43
|
+
assert ScrubyModel in class_model.__bases__, (
|
|
44
|
+
"Scruby => Argument `class_model` does not contain the base class `ScrubyModel`."
|
|
45
|
+
)
|
|
46
|
+
assert "key" in list(class_model.model_fields.keys()), (
|
|
47
|
+
f"Model: {class_model.__name__} => The `key` field is missing."
|
|
48
|
+
)
|
|
49
|
+
|
|
61
50
|
super().__init__()
|
|
62
|
-
self.
|
|
51
|
+
self._class_model = class_model
|
|
63
52
|
self._db_id = ScrubyConfig.db_id
|
|
64
53
|
self._db_root = ScrubyConfig.db_root
|
|
65
54
|
self._hash_reduce_left = ScrubyConfig.HASH_REDUCE_LEFT
|
|
66
55
|
self._max_number_branch = ScrubyConfig.MAX_NUMBER_BRANCH
|
|
67
56
|
self._max_workers = ScrubyConfig.max_workers
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
async def collection(cls, class_model: Any) -> Any:
|
|
71
|
-
"""Asynchronous method for creating a new collection and accessing an existing collection.
|
|
72
|
-
|
|
73
|
-
Args:
|
|
74
|
-
class_model (Any): Class of Model (ScrubyModel).
|
|
75
|
-
|
|
76
|
-
Returns:
|
|
77
|
-
Instance of Scruby for access a collection.
|
|
78
|
-
"""
|
|
79
|
-
if __debug__:
|
|
80
|
-
# Check if the object belongs to the class `ScrubyModel`
|
|
81
|
-
if ScrubyModel not in class_model.__bases__:
|
|
82
|
-
msg = (
|
|
83
|
-
"Method: `collection` => argument `class_model` " + "does not contain the base class `ScrubyModel`!"
|
|
84
|
-
)
|
|
85
|
-
raise AssertionError(msg)
|
|
86
|
-
# Checking the model for the presence of a key.
|
|
87
|
-
model_fields = list(class_model.model_fields.keys())
|
|
88
|
-
if "key" not in model_fields:
|
|
89
|
-
msg = f"Model: {class_model.__name__} => The `key` field is missing!"
|
|
90
|
-
raise AssertionError(msg)
|
|
91
|
-
if "created_at" not in model_fields:
|
|
92
|
-
msg = f"Model: {class_model.__name__} => The `created_at` field is missing!"
|
|
93
|
-
raise AssertionError(msg)
|
|
94
|
-
if "updated_at" not in model_fields:
|
|
95
|
-
msg = f"Model: {class_model.__name__} => The `updated_at` field is missing!"
|
|
96
|
-
raise AssertionError(msg)
|
|
97
|
-
# Check the length of the collection name for an acceptable size.
|
|
98
|
-
len_db_root_absolut_path = len(str(await Path(ScrubyConfig.db_root).resolve()).encode("utf-8"))
|
|
99
|
-
len_model_name = len(class_model.__name__)
|
|
100
|
-
len_full_path_leaf = len_db_root_absolut_path + len_model_name + 26
|
|
101
|
-
if len_full_path_leaf > 255:
|
|
102
|
-
excess = len_full_path_leaf - 255
|
|
103
|
-
msg = (
|
|
104
|
-
f"Model: {class_model.__name__} => The collection name is too long, "
|
|
105
|
-
+ f"it exceeds the limit of {excess} characters!"
|
|
106
|
-
)
|
|
107
|
-
raise AssertionError(msg)
|
|
108
|
-
# Create instance of Scruby
|
|
109
|
-
instance = cls()
|
|
110
|
-
# Add model class to Scruby
|
|
111
|
-
instance.__dict__["_class_model"] = class_model
|
|
112
|
-
# Create a path for metadata.
|
|
113
|
-
meta_dir_path_tuple = (
|
|
57
|
+
self._meta = Meta
|
|
58
|
+
self._meta_path = Path(
|
|
114
59
|
ScrubyConfig.db_root,
|
|
115
60
|
class_model.__name__,
|
|
116
61
|
"meta",
|
|
117
|
-
)
|
|
118
|
-
instance.__dict__["_meta_path"] = Path(
|
|
119
|
-
*meta_dir_path_tuple,
|
|
120
62
|
"meta.json",
|
|
121
63
|
)
|
|
122
|
-
# Create metadata for collection, if missing.
|
|
123
|
-
meta_dir_path = Path(*meta_dir_path_tuple)
|
|
124
|
-
if not await meta_dir_path.exists():
|
|
125
|
-
# Create metadata.
|
|
126
|
-
await meta_dir_path.mkdir(parents=True)
|
|
127
|
-
meta = _Meta(
|
|
128
|
-
collection_name=class_model.__name__,
|
|
129
|
-
hash_reduce_left=instance.__dict__["_hash_reduce_left"],
|
|
130
|
-
max_number_branch=instance.__dict__["_max_number_branch"],
|
|
131
|
-
counter_documents=0,
|
|
132
|
-
)
|
|
133
|
-
# Save metadata of collection.
|
|
134
|
-
meta_json = meta.model_dump_json()
|
|
135
|
-
meta_path = Path(*(meta_dir_path, "meta.json"))
|
|
136
|
-
await meta_path.write_text(meta_json, "utf-8")
|
|
137
|
-
# Create a cache structure for the collection.
|
|
138
|
-
if instance.__dict__["_hash_reduce_left"] != 0:
|
|
139
|
-
DocCache.create_structure(class_model.__name__)
|
|
140
64
|
# Plugins connection.
|
|
141
65
|
plugin_list: dict[str, Any] = {}
|
|
142
66
|
if ScrubyConfig.plugins is not None:
|
|
143
67
|
for plugin in ScrubyConfig.plugins:
|
|
144
68
|
name = plugin.__name__
|
|
145
69
|
name = name[0].lower() + name[1:]
|
|
146
|
-
plugin_list[name] = plugin(scruby_self=
|
|
147
|
-
|
|
148
|
-
return instance
|
|
70
|
+
plugin_list[name] = plugin(scruby_self=self)
|
|
71
|
+
self.plugins = NamedTuple(**plugin_list)
|
|
149
72
|
|
|
150
|
-
async def get_meta(self) ->
|
|
73
|
+
async def get_meta(self) -> Meta:
|
|
151
74
|
"""Asynchronous method for getting metadata of collection.
|
|
152
75
|
|
|
153
76
|
This method is for internal use.
|
|
@@ -156,10 +79,10 @@ class Scruby(
|
|
|
156
79
|
Metadata object.
|
|
157
80
|
"""
|
|
158
81
|
meta_json = await self._meta_path.read_text()
|
|
159
|
-
meta:
|
|
82
|
+
meta: Meta = self._meta.model_validate_json(meta_json)
|
|
160
83
|
return meta
|
|
161
84
|
|
|
162
|
-
async def _set_meta(self, meta:
|
|
85
|
+
async def _set_meta(self, meta: Meta) -> None:
|
|
163
86
|
"""Asynchronous method for updating metadata of collection.
|
|
164
87
|
|
|
165
88
|
This method is for internal use.
|
|
@@ -186,7 +109,7 @@ class Scruby(
|
|
|
186
109
|
"""
|
|
187
110
|
meta_path = self._meta_path
|
|
188
111
|
meta_json = await meta_path.read_text("utf-8")
|
|
189
|
-
meta:
|
|
112
|
+
meta: Meta = self._meta.model_validate_json(meta_json)
|
|
190
113
|
meta.counter_documents += step
|
|
191
114
|
meta_json = meta.model_dump_json()
|
|
192
115
|
await meta_path.write_text(meta_json, "utf-8")
|
scruby/meta.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Meta.
|
|
2
|
+
|
|
3
|
+
Metadata management.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
__all__ = (
|
|
9
|
+
"Meta",
|
|
10
|
+
"Metadata",
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from pydantic import BaseModel
|
|
16
|
+
|
|
17
|
+
from scruby.config import ScrubyConfig
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Meta(BaseModel):
|
|
21
|
+
"""Structure of metadata for collection."""
|
|
22
|
+
|
|
23
|
+
collection_name: str
|
|
24
|
+
hash_reduce_left: int
|
|
25
|
+
max_number_branch: int
|
|
26
|
+
counter_documents: int
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Metadata:
|
|
30
|
+
"""Metadata management."""
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def create(collection_name: str) -> None:
|
|
34
|
+
"""Create metadata for collection.
|
|
35
|
+
|
|
36
|
+
Args:
|
|
37
|
+
collection_name (str): Collection name.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
None.
|
|
41
|
+
"""
|
|
42
|
+
meta_dir_path = Path(
|
|
43
|
+
ScrubyConfig.db_root,
|
|
44
|
+
collection_name,
|
|
45
|
+
"meta",
|
|
46
|
+
)
|
|
47
|
+
if not meta_dir_path.exists():
|
|
48
|
+
meta_dir_path.mkdir(parents=True)
|
|
49
|
+
meta = Meta(
|
|
50
|
+
collection_name=collection_name,
|
|
51
|
+
hash_reduce_left=ScrubyConfig.HASH_REDUCE_LEFT,
|
|
52
|
+
max_number_branch=ScrubyConfig.MAX_NUMBER_BRANCH,
|
|
53
|
+
counter_documents=0,
|
|
54
|
+
)
|
|
55
|
+
meta_json = meta.model_dump_json()
|
|
56
|
+
meta_path = Path(meta_dir_path, "meta.json")
|
|
57
|
+
meta_path.write_text(meta_json, "utf-8")
|
scruby/mixins/collection.py
CHANGED
|
@@ -11,10 +11,10 @@ __all__ = ("Collection",)
|
|
|
11
11
|
from shutil import rmtree
|
|
12
12
|
from typing import final
|
|
13
13
|
|
|
14
|
-
from anyio import Path, to_thread
|
|
15
|
-
|
|
16
14
|
from scruby.cache import DocCache
|
|
17
15
|
from scruby.config import ScrubyConfig
|
|
16
|
+
from scruby.meta import Metadata
|
|
17
|
+
from scruby.model import ScrubyModel
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class Collection:
|
|
@@ -31,18 +31,15 @@ class Collection:
|
|
|
31
31
|
|
|
32
32
|
@final
|
|
33
33
|
@staticmethod
|
|
34
|
-
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
all_entries = Path.iterdir(db_directory)
|
|
39
|
-
directory_names: list[str] = [entry.name async for entry in all_entries if entry.name != ".env.meta"]
|
|
40
|
-
return directory_names or None
|
|
34
|
+
def collection_list() -> list[str] | None:
|
|
35
|
+
"""Synchronous method for getting collection list."""
|
|
36
|
+
collections = [item.__name__ for item in ScrubyModel.__subclasses__()]
|
|
37
|
+
return collections or None
|
|
41
38
|
|
|
42
39
|
@final
|
|
43
40
|
@staticmethod
|
|
44
|
-
|
|
45
|
-
"""
|
|
41
|
+
def clear_collection(collection_name: str) -> None:
|
|
42
|
+
"""Synchronous method to remove all documents from a collection.
|
|
46
43
|
|
|
47
44
|
Args:
|
|
48
45
|
collection_name (str): Collection name.
|
|
@@ -50,8 +47,14 @@ class Collection:
|
|
|
50
47
|
Returns:
|
|
51
48
|
None.
|
|
52
49
|
"""
|
|
50
|
+
# Clear collection on file system
|
|
53
51
|
target_directory = f"{ScrubyConfig.db_root}/{collection_name}"
|
|
54
|
-
|
|
52
|
+
rmtree(target_directory)
|
|
53
|
+
# Create metadata for collection
|
|
54
|
+
Metadata.create(collection_name)
|
|
55
|
+
|
|
56
|
+
# Clear collection in cache
|
|
55
57
|
if ScrubyConfig.HASH_REDUCE_LEFT != 0:
|
|
56
58
|
del DocCache.cache[collection_name]
|
|
59
|
+
DocCache.create_structure(collection_name)
|
|
57
60
|
return
|
scruby/model.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""Model."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = ("ScrubyModel",)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
|
|
10
|
+
from pydantic import BaseModel
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ScrubyModel(BaseModel):
|
|
14
|
+
"""Additional fields for models."""
|
|
15
|
+
|
|
16
|
+
created_at: datetime | None = None
|
|
17
|
+
updated_at: datetime | None = None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: scruby
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Asynchronous library for building and managing a hybrid database, by scheme of key-value.
|
|
5
5
|
Project-URL: Bug Tracker, https://github.com/kebasyaty/scruby/issues
|
|
6
6
|
Project-URL: Changelog, https://github.com/kebasyaty/scruby/blob/v2/CHANGELOG.md
|
|
@@ -165,7 +165,7 @@ async def main() -> None:
|
|
|
165
165
|
Scruby.run()
|
|
166
166
|
|
|
167
167
|
# Get/Create a User collection
|
|
168
|
-
user_coll =
|
|
168
|
+
user_coll = Scruby(User)
|
|
169
169
|
|
|
170
170
|
# Create user
|
|
171
171
|
user = User(
|
|
@@ -196,7 +196,7 @@ async def main() -> None:
|
|
|
196
196
|
user_coll.collection_name() # => User
|
|
197
197
|
|
|
198
198
|
# Get collection list
|
|
199
|
-
coll_list =
|
|
199
|
+
coll_list = Scruby.collection_list() # => ["User"]
|
|
200
200
|
|
|
201
201
|
# Get the number of documents in the collection from metadata
|
|
202
202
|
await user_coll.estimated_document_count() # => 1
|
|
@@ -204,8 +204,8 @@ async def main() -> None:
|
|
|
204
204
|
# Get the number of documents comparable to the filter
|
|
205
205
|
user_coll.count_documents(filter_fn=lambda doc: doc.first_name == "John") == 1
|
|
206
206
|
|
|
207
|
-
#
|
|
208
|
-
|
|
207
|
+
# Clear collection
|
|
208
|
+
Scruby.clear_collection("User")
|
|
209
209
|
|
|
210
210
|
# Full database deletion
|
|
211
211
|
# Hint: The main purpose is tests
|
|
@@ -248,7 +248,7 @@ async def main() -> None:
|
|
|
248
248
|
Scruby.run()
|
|
249
249
|
|
|
250
250
|
# Get/Create a Phone collection
|
|
251
|
-
phone_coll =
|
|
251
|
+
phone_coll = Scruby(Phone)
|
|
252
252
|
|
|
253
253
|
# Create phone
|
|
254
254
|
phone = Phone(
|
|
@@ -325,7 +325,7 @@ async def main() -> None:
|
|
|
325
325
|
Scruby.run()
|
|
326
326
|
|
|
327
327
|
# Get/Create a Car collection
|
|
328
|
-
car_coll =
|
|
328
|
+
car_coll = Scruby(Car)
|
|
329
329
|
|
|
330
330
|
# Create cars
|
|
331
331
|
for num in range(1, 10):
|
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
scruby/__init__.py,sha256=
|
|
1
|
+
scruby/__init__.py,sha256=YIR8zFx0GmS8ha96-MU9yCgKeYNIh1K8idlQckfg21E,1324
|
|
2
2
|
scruby/aggregation.py,sha256=NBFxQqyRqUG2KIuD9fbl4uzSHJWTaskjiZ1YNBa-Zbo,3575
|
|
3
|
-
scruby/cache.py,sha256=
|
|
3
|
+
scruby/cache.py,sha256=wGnjuFlpA3sRGfp4vv3o3nXR_tOC8HtuzFv1TyrKEyk,3997
|
|
4
4
|
scruby/config.py,sha256=INAFqNAeF8BifIywjElC97rawfTLuqpRfieUdAO7A6k,5122
|
|
5
|
-
scruby/db.py,sha256=
|
|
5
|
+
scruby/db.py,sha256=3erS6kDcjNy6KBx_8x58P6lI_2DzaIkYg3DK5_NQ_iY,6796
|
|
6
6
|
scruby/errors.py,sha256=lTWiHzyO5Es9Nkf7quODJjONGn6ifcL95qlpA4epQQM,1386
|
|
7
|
+
scruby/meta.py,sha256=dfr8q3TWhVcEl-lZuH5yd9rbO9vAPBhXBO3VYCNcOWo,1283
|
|
8
|
+
scruby/model.py,sha256=1infUd1G-zxj5Z93mNOp1Wi2anD6TKDgu5Y0KEVcH2k,292
|
|
7
9
|
scruby/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
10
|
scruby/utils.py,sha256=ZwWxSyh_BAOQkXlIqXFOlX2lHVk9rfYGQiVqtTk8PpE,1865
|
|
9
11
|
scruby/mixins/__init__.py,sha256=nT79e80zXliuTGhR9CFosI2-3PQUCKwXbR7wPiFwgrU,669
|
|
10
|
-
scruby/mixins/collection.py,sha256=
|
|
12
|
+
scruby/mixins/collection.py,sha256=3Gn9zIT-WnSCBd-IgpE9tobRlW675YX__qaWoUOWIJM,1758
|
|
11
13
|
scruby/mixins/count.py,sha256=CGpyOpsG25uC5utI2ByNxq2iTbJocj6w5tXrC1_cP40,2561
|
|
12
14
|
scruby/mixins/custom_task.py,sha256=DIry4gBlTdaqZqymar-RrHSdhEiC2tn2pl9jmbk56zw,1547
|
|
13
15
|
scruby/mixins/delete.py,sha256=InKVIud_ZYx9-CchUz_IygQDXMynNi4jQ0HKYeHC_R8,4328
|
|
14
16
|
scruby/mixins/find.py,sha256=oEZRE6RqIBdwvNr8iSYyod8hI6ibi_egJP0pyXmJiss,9169
|
|
15
17
|
scruby/mixins/keys.py,sha256=MvBy_8fQGROaQATK2qse2V8wR-xodPQG0KKZ2PK_t9U,10790
|
|
16
18
|
scruby/mixins/update.py,sha256=TyxvxB-gNijlfzTmhwrq0ydvu0C3R-RihW5h5tJ96bM,4964
|
|
17
|
-
scruby-2.
|
|
18
|
-
scruby-2.
|
|
19
|
-
scruby-2.
|
|
20
|
-
scruby-2.
|
|
21
|
-
scruby-2.
|
|
19
|
+
scruby-2.3.0.dist-info/METADATA,sha256=mtGCSIcs5Ah_xokLG3OnmnjDJGeDGBSFp0iNNT8DxUk,13382
|
|
20
|
+
scruby-2.3.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
21
|
+
scruby-2.3.0.dist-info/licenses/GPL-3.0-LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
22
|
+
scruby-2.3.0.dist-info/licenses/MIT-LICENSE,sha256=mS0Wz0yGNB63gEcWEnuIb_lldDYV0sjRaO-o_GL6CWE,1074
|
|
23
|
+
scruby-2.3.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|