ramifice 0.3.19__py3-none-any.whl → 0.3.21__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.
- ramifice/decorators.py +20 -13
- ramifice/fields/file_field.py +3 -3
- ramifice/fields/image_field.py +5 -7
- ramifice/model.py +1 -21
- ramifice/pseudo_model.py +183 -0
- {ramifice-0.3.19.dist-info → ramifice-0.3.21.dist-info}/METADATA +1 -1
- {ramifice-0.3.19.dist-info → ramifice-0.3.21.dist-info}/RECORD +9 -8
- {ramifice-0.3.19.dist-info → ramifice-0.3.21.dist-info}/WHEEL +0 -0
- {ramifice-0.3.19.dist-info → ramifice-0.3.21.dist-info}/licenses/LICENSE +0 -0
ramifice/decorators.py
CHANGED
@@ -12,6 +12,7 @@ from .hooks import HooksMixin
|
|
12
12
|
from .indexing import IndexMixin
|
13
13
|
from .model import Model
|
14
14
|
from .paladins import CheckMixin, QPaladinsMixin, ValidationMixin # type: ignore[attr-defined]
|
15
|
+
from .pseudo_model import PseudoModel
|
15
16
|
from .store import REGEX
|
16
17
|
|
17
18
|
|
@@ -55,7 +56,10 @@ def model(
|
|
55
56
|
raise PanicError(msg)
|
56
57
|
|
57
58
|
attrs = {key: val for key, val in cls.__dict__.items()}
|
58
|
-
|
59
|
+
if is_migrate_model:
|
60
|
+
attrs["__dict__"] = Model.__dict__["__dict__"]
|
61
|
+
else:
|
62
|
+
attrs["__dict__"] = PseudoModel.__dict__["__dict__"]
|
59
63
|
metadata = {
|
60
64
|
"service_name": service_name,
|
61
65
|
"fixture_name": fixture_name,
|
@@ -65,7 +69,10 @@ def model(
|
|
65
69
|
"is_update_doc": is_update_doc if is_migrate_model else False,
|
66
70
|
"is_delete_doc": is_delete_doc if is_migrate_model else False,
|
67
71
|
}
|
68
|
-
attrs["META"] = {
|
72
|
+
attrs["META"] = {
|
73
|
+
**metadata,
|
74
|
+
**caching(cls, service_name, is_migrate_model),
|
75
|
+
}
|
69
76
|
|
70
77
|
if is_migrate_model:
|
71
78
|
return type(
|
@@ -84,7 +91,7 @@ def model(
|
|
84
91
|
return type(
|
85
92
|
cls.__name__,
|
86
93
|
(
|
87
|
-
|
94
|
+
PseudoModel,
|
88
95
|
ValidationMixin,
|
89
96
|
CheckMixin,
|
90
97
|
AddValidMixin,
|
@@ -95,7 +102,7 @@ def model(
|
|
95
102
|
return decorator
|
96
103
|
|
97
104
|
|
98
|
-
def caching(cls: Any, service_name: str) -> dict[str, Any]:
|
105
|
+
def caching(cls: Any, service_name: str, is_migrate_model: bool) -> dict[str, Any]:
|
99
106
|
"""Add additional metadata to `Model.META`."""
|
100
107
|
metadata: dict[str, Any] = {}
|
101
108
|
model_name = cls.__name__
|
@@ -117,14 +124,13 @@ def caching(cls: Any, service_name: str) -> dict[str, Any]:
|
|
117
124
|
# Count fields for migrating.
|
118
125
|
count_fields_for_migrating = 0
|
119
126
|
|
120
|
-
|
121
|
-
|
122
|
-
default_fields: dict[str, Any] = {
|
123
|
-
|
124
|
-
"created_at"
|
125
|
-
"updated_at"
|
126
|
-
}
|
127
|
-
fields = {**old_model.__dict__, **default_fields}
|
127
|
+
raw_model = cls()
|
128
|
+
raw_model.fields()
|
129
|
+
default_fields: dict[str, Any] = {"_id": IDField()}
|
130
|
+
if is_migrate_model:
|
131
|
+
default_fields["created_at"] = DateTimeField()
|
132
|
+
default_fields["updated_at"] = DateTimeField()
|
133
|
+
fields = {**raw_model.__dict__, **default_fields}
|
128
134
|
for f_name, f_type in fields.items():
|
129
135
|
if not callable(f_type):
|
130
136
|
f_type_str = f_type.__class__.__name__
|
@@ -138,7 +144,8 @@ def caching(cls: Any, service_name: str) -> dict[str, Any]:
|
|
138
144
|
#
|
139
145
|
if not f_type.ignored:
|
140
146
|
# Count fields for migrating.
|
141
|
-
|
147
|
+
if is_migrate_model:
|
148
|
+
count_fields_for_migrating += 1
|
142
149
|
# Get a dictionary of field names and types.
|
143
150
|
field_name_and_type[f_name] = f_type_str
|
144
151
|
# Build data migration storage for dynamic fields.
|
ramifice/fields/file_field.py
CHANGED
@@ -4,7 +4,7 @@ import os
|
|
4
4
|
import shutil
|
5
5
|
import uuid
|
6
6
|
from base64 import b64decode
|
7
|
-
from datetime import
|
7
|
+
from datetime import date
|
8
8
|
from pathlib import Path
|
9
9
|
from typing import Any
|
10
10
|
|
@@ -121,7 +121,7 @@ class FileField(Field, FileGroup, JsonMixin):
|
|
121
121
|
# Create new (uuid) file name.
|
122
122
|
f_uuid_name = f"{uuid.uuid4()}{extension}"
|
123
123
|
# Create the current date for the directory name.
|
124
|
-
date_str =
|
124
|
+
date_str: str = str(date.today())
|
125
125
|
# Create path to target directory.
|
126
126
|
dir_target_path = f"{self.media_root}/{self.target_dir}/{date_str}"
|
127
127
|
# Create target directory if it does not exist.
|
@@ -166,7 +166,7 @@ class FileField(Field, FileGroup, JsonMixin):
|
|
166
166
|
# Create new (uuid) file name.
|
167
167
|
f_uuid_name = f"{uuid.uuid4()}{extension}"
|
168
168
|
# Create the current date for the directory name.
|
169
|
-
date_str =
|
169
|
+
date_str: str = str(date.today())
|
170
170
|
# Create path to target directory.
|
171
171
|
dir_target_path = f"{self.media_root}/{self.target_dir}/{date_str}"
|
172
172
|
# Create target directory if it does not exist.
|
ramifice/fields/image_field.py
CHANGED
@@ -4,7 +4,7 @@ import os
|
|
4
4
|
import shutil
|
5
5
|
import uuid
|
6
6
|
from base64 import b64decode
|
7
|
-
from datetime import
|
7
|
+
from datetime import date
|
8
8
|
from pathlib import Path
|
9
9
|
from typing import Any
|
10
10
|
|
@@ -135,7 +135,6 @@ class ImageField(Field, FileGroup, JsonMixin):
|
|
135
135
|
base64_str: str | None = None,
|
136
136
|
filename: str | None = None,
|
137
137
|
is_delete: bool = False,
|
138
|
-
add_wh: bool = False,
|
139
138
|
) -> None:
|
140
139
|
"""Convert base64 to a image,
|
141
140
|
get image information and save in the target directory.
|
@@ -159,7 +158,7 @@ class ImageField(Field, FileGroup, JsonMixin):
|
|
159
158
|
if item[0] == 40:
|
160
159
|
break
|
161
160
|
# Create the current date for the directory name.
|
162
|
-
date_str =
|
161
|
+
date_str: str = str(date.today())
|
163
162
|
# Directory name for the original image and its thumbnails.
|
164
163
|
general_dir = uuid.uuid4()
|
165
164
|
# Create path to target directory with images.
|
@@ -181,7 +180,7 @@ class ImageField(Field, FileGroup, JsonMixin):
|
|
181
180
|
img_info["path"] = main_img_path
|
182
181
|
img_info["url"] = f"{imgs_dir_url}/{new_original_name}"
|
183
182
|
# Add width and height.
|
184
|
-
if
|
183
|
+
if self.__dict__.get("add_width_height", False):
|
185
184
|
with Image.open(main_img_path) as img:
|
186
185
|
width, height = img.size
|
187
186
|
img_info["width"] = width
|
@@ -209,7 +208,6 @@ class ImageField(Field, FileGroup, JsonMixin):
|
|
209
208
|
self,
|
210
209
|
src_path: str | None = None,
|
211
210
|
is_delete: bool = False,
|
212
|
-
add_wh: bool = False,
|
213
211
|
) -> None:
|
214
212
|
"""Get image information and copy the image to the target directory."""
|
215
213
|
src_path = src_path or None
|
@@ -224,7 +222,7 @@ class ImageField(Field, FileGroup, JsonMixin):
|
|
224
222
|
msg = f"The image `{src_path}` has no extension."
|
225
223
|
raise FileHasNoExtensionError(msg)
|
226
224
|
# Create the current date for the directory name.
|
227
|
-
date_str =
|
225
|
+
date_str: str = str(date.today())
|
228
226
|
# Directory name for the original image and its thumbnails.
|
229
227
|
general_dir = uuid.uuid4()
|
230
228
|
# Create path to target directory with images.
|
@@ -244,7 +242,7 @@ class ImageField(Field, FileGroup, JsonMixin):
|
|
244
242
|
img_info["path"] = main_img_path
|
245
243
|
img_info["url"] = f"{imgs_dir_url}/{new_original_name}"
|
246
244
|
# Add width and height.
|
247
|
-
if
|
245
|
+
if self.__dict__.get("add_width_height", False):
|
248
246
|
with Image.open(main_img_path) as img:
|
249
247
|
width, height = img.size
|
250
248
|
img_info["width"] = width
|
ramifice/model.py
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
"""For converting Python classes into Ramifice Model."""
|
2
2
|
|
3
3
|
import json
|
4
|
-
import os
|
5
|
-
import shutil
|
6
4
|
from abc import ABCMeta, abstractmethod
|
7
5
|
from typing import Any
|
8
6
|
|
@@ -15,7 +13,7 @@ from .fields import DateTimeField, IDField # type: ignore[attr-defined]
|
|
15
13
|
|
16
14
|
|
17
15
|
class Model(metaclass=ABCMeta):
|
18
|
-
"""For converting Python
|
16
|
+
"""For converting Python Class into Ramifice Model."""
|
19
17
|
|
20
18
|
META: dict[str, Any] = {}
|
21
19
|
|
@@ -47,24 +45,6 @@ class Model(metaclass=ABCMeta):
|
|
47
45
|
self.fields()
|
48
46
|
self.inject()
|
49
47
|
|
50
|
-
def __del__(self) -> None: # noqa: D105
|
51
|
-
# If the model is not migrated,
|
52
|
-
# it must delete files and images in the destructor.
|
53
|
-
if not self.__class__.META["is_migrate_model"]:
|
54
|
-
for _, f_type in self.__dict__.items():
|
55
|
-
if not callable(f_type):
|
56
|
-
value = f_type.value
|
57
|
-
if value is None:
|
58
|
-
continue
|
59
|
-
if f_type.group == "file":
|
60
|
-
value = value.get("path")
|
61
|
-
if value is not None:
|
62
|
-
os.remove(value)
|
63
|
-
elif f_type.group == "img":
|
64
|
-
value = value.get("imgs_dir_path")
|
65
|
-
if value is not None:
|
66
|
-
shutil.rmtree(value)
|
67
|
-
|
68
48
|
@abstractmethod
|
69
49
|
def fields(self) -> None:
|
70
50
|
"""For adding fields."""
|
ramifice/pseudo_model.py
ADDED
@@ -0,0 +1,183 @@
|
|
1
|
+
"""For converting Python classes into Ramifice Model."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
import os
|
5
|
+
import shutil
|
6
|
+
from abc import ABCMeta, abstractmethod
|
7
|
+
from typing import Any
|
8
|
+
|
9
|
+
from babel.dates import format_date, format_datetime
|
10
|
+
from bson.objectid import ObjectId
|
11
|
+
from dateutil.parser import parse
|
12
|
+
|
13
|
+
from . import translations
|
14
|
+
from .fields import IDField # type: ignore[attr-defined]
|
15
|
+
|
16
|
+
|
17
|
+
class PseudoModel(metaclass=ABCMeta):
|
18
|
+
"""For convert the Python class into a pseudo model Ramifice.
|
19
|
+
|
20
|
+
Used for a Model that do not migrate into the database.
|
21
|
+
"""
|
22
|
+
|
23
|
+
META: dict[str, Any] = {}
|
24
|
+
|
25
|
+
def __init__(self) -> None: # noqa: D107
|
26
|
+
self._id = IDField(
|
27
|
+
label="Stub",
|
28
|
+
placeholder="Stub",
|
29
|
+
hint="Stub",
|
30
|
+
hide=True,
|
31
|
+
disabled=True,
|
32
|
+
)
|
33
|
+
self.fields()
|
34
|
+
self.inject()
|
35
|
+
for _, f_type in self.__dict__.items():
|
36
|
+
if not callable(f_type):
|
37
|
+
if f_type.group == "img":
|
38
|
+
f_type.__dict__["add_width_height"] = True
|
39
|
+
|
40
|
+
def __del__(self) -> None: # noqa: D105
|
41
|
+
# If the model is not migrated,
|
42
|
+
# it must delete files and images in the destructor.
|
43
|
+
for _, f_type in self.__dict__.items():
|
44
|
+
if not callable(f_type):
|
45
|
+
value = f_type.value
|
46
|
+
if value is None:
|
47
|
+
continue
|
48
|
+
if f_type.group == "file":
|
49
|
+
value = value.get("path")
|
50
|
+
if value is not None:
|
51
|
+
os.remove(value)
|
52
|
+
elif f_type.group == "img":
|
53
|
+
value = value.get("imgs_dir_path")
|
54
|
+
if value is not None:
|
55
|
+
shutil.rmtree(value)
|
56
|
+
|
57
|
+
@abstractmethod
|
58
|
+
def fields(self) -> None:
|
59
|
+
"""For adding fields."""
|
60
|
+
pass
|
61
|
+
|
62
|
+
def model_name(self) -> str:
|
63
|
+
"""Get Model name - Class name."""
|
64
|
+
return self.__class__.__name__
|
65
|
+
|
66
|
+
def full_model_name(self) -> str:
|
67
|
+
"""Get full Model name - module_name + . + ClassName."""
|
68
|
+
cls = self.__class__
|
69
|
+
return f"{cls.__module__}.{cls.__name__}"
|
70
|
+
|
71
|
+
def inject(self) -> None:
|
72
|
+
"""Injecting metadata from Model.META in params of fields."""
|
73
|
+
metadata = self.__class__.META
|
74
|
+
if bool(metadata):
|
75
|
+
field_attrs = metadata["field_attrs"]
|
76
|
+
data_dynamic_fields = metadata["data_dynamic_fields"]
|
77
|
+
for f_name, f_type in self.__dict__.items():
|
78
|
+
if not callable(f_type):
|
79
|
+
f_type.id = field_attrs[f_name]["id"]
|
80
|
+
f_type.name = field_attrs[f_name]["name"]
|
81
|
+
if "Dyn" in f_type.field_type:
|
82
|
+
f_type.choices = data_dynamic_fields[f_name]
|
83
|
+
|
84
|
+
# Complect of methods for converting Model to JSON and back.
|
85
|
+
# --------------------------------------------------------------------------
|
86
|
+
def to_dict(self) -> dict[str, Any]:
|
87
|
+
"""Convert object instance to a dictionary."""
|
88
|
+
json_dict: dict[str, Any] = {}
|
89
|
+
for name, data in self.__dict__.items():
|
90
|
+
if not callable(data):
|
91
|
+
json_dict[name] = data.to_dict()
|
92
|
+
return json_dict
|
93
|
+
|
94
|
+
def to_json(self) -> str:
|
95
|
+
"""Convert object instance to a JSON string."""
|
96
|
+
return json.dumps(self.to_dict())
|
97
|
+
|
98
|
+
@classmethod
|
99
|
+
def from_dict(cls, json_dict: dict[str, Any]) -> Any:
|
100
|
+
"""Convert JSON string to a object instance."""
|
101
|
+
obj = cls()
|
102
|
+
for name, data in json_dict.items():
|
103
|
+
obj.__dict__[name] = obj.__dict__[name].__class__.from_dict(data)
|
104
|
+
return obj
|
105
|
+
|
106
|
+
@classmethod
|
107
|
+
def from_json(cls, json_str: str) -> Any:
|
108
|
+
"""Convert JSON string to a object instance."""
|
109
|
+
json_dict = json.loads(json_str)
|
110
|
+
return cls.from_dict(json_dict)
|
111
|
+
|
112
|
+
# --------------------------------------------------------------------------
|
113
|
+
def to_dict_only_value(self) -> dict[str, Any]:
|
114
|
+
"""Convert model.field.value (only the `value` attribute) to a dictionary."""
|
115
|
+
json_dict: dict[str, Any] = {}
|
116
|
+
current_locale = translations.CURRENT_LOCALE
|
117
|
+
for name, data in self.__dict__.items():
|
118
|
+
if callable(data):
|
119
|
+
continue
|
120
|
+
value = data.value
|
121
|
+
if value is not None:
|
122
|
+
group = data.group
|
123
|
+
if group == "date":
|
124
|
+
value = (
|
125
|
+
format_date(
|
126
|
+
date=value,
|
127
|
+
format="short",
|
128
|
+
locale=current_locale,
|
129
|
+
)
|
130
|
+
if data.field_type == "DateField"
|
131
|
+
else format_datetime(
|
132
|
+
datetime=value,
|
133
|
+
format="short",
|
134
|
+
locale=current_locale,
|
135
|
+
)
|
136
|
+
)
|
137
|
+
elif group == "id":
|
138
|
+
value = str(value)
|
139
|
+
elif group == "pass":
|
140
|
+
value = None
|
141
|
+
json_dict[name] = value
|
142
|
+
return json_dict
|
143
|
+
|
144
|
+
def to_json_only_value(self) -> str:
|
145
|
+
"""Convert model.field.value (only the `value` attribute) to a JSON string."""
|
146
|
+
return json.dumps(self.to_dict_only_value())
|
147
|
+
|
148
|
+
@classmethod
|
149
|
+
def from_dict_only_value(cls, json_dict: dict[str, Any]) -> Any:
|
150
|
+
"""Convert JSON string to a object instance."""
|
151
|
+
obj = cls()
|
152
|
+
for name, data in obj.__dict__.items():
|
153
|
+
if callable(data):
|
154
|
+
continue
|
155
|
+
value = json_dict.get(name)
|
156
|
+
if value is not None:
|
157
|
+
group = data.group
|
158
|
+
if group == "date":
|
159
|
+
value = parse(value)
|
160
|
+
elif group == "id":
|
161
|
+
value = ObjectId(value)
|
162
|
+
obj.__dict__[name].value = value
|
163
|
+
return obj
|
164
|
+
|
165
|
+
@classmethod
|
166
|
+
def from_json_only_value(cls, json_str: str) -> Any:
|
167
|
+
"""Convert JSON string to a object instance."""
|
168
|
+
json_dict = json.loads(json_str)
|
169
|
+
return cls.from_dict_only_value(json_dict)
|
170
|
+
|
171
|
+
def refrash_fields(self, only_value_dict: dict[str, Any]) -> None:
|
172
|
+
"""Partial or complete update a `value` of fields."""
|
173
|
+
for name, data in self.__dict__.items():
|
174
|
+
if callable(data):
|
175
|
+
continue
|
176
|
+
value = only_value_dict.get(name)
|
177
|
+
if value is not None:
|
178
|
+
group = data.group
|
179
|
+
if group == "date":
|
180
|
+
value = parse(value)
|
181
|
+
elif group == "id":
|
182
|
+
value = ObjectId(value)
|
183
|
+
self.__dict__[name].value = value
|
@@ -1,13 +1,14 @@
|
|
1
1
|
ramifice/__init__.py,sha256=ISlaL2BprlJLE_N1fvtAqGrB3Dhniy9IZGoyWEYZhRU,678
|
2
2
|
ramifice/add_valid.py,sha256=kvpMg7snL9tor0A23XRdgwiXazRwHfb8baoJUNjM_4Y,327
|
3
|
-
ramifice/decorators.py,sha256=
|
3
|
+
ramifice/decorators.py,sha256=X4UnHrbuMObv10dooNip3u9ihfdNvFqeMcfLNuhLlbg,6466
|
4
4
|
ramifice/errors.py,sha256=iuhq7fzpUmsOyeXeg2fJjta8yAuqlXLKsZVMpfUhtHE,1901
|
5
5
|
ramifice/fixtures.py,sha256=NtxOnZslYJb4yvRpZbs3ckugmTwHQFS_9iCt2zddOC0,3102
|
6
6
|
ramifice/hooks.py,sha256=Ri-ISfMT-IHaLO2eAqg8CODCTs3HRTxSylqspUKnVf4,873
|
7
7
|
ramifice/indexing.py,sha256=wQpX2qei5Zc7iIq5yIV93Skp8Aa8ZD0vybnEk7cRuXs,271
|
8
8
|
ramifice/migration.py,sha256=t_Rm1OUQYrlaPQQd1uS5S7EYMvSuKUcWzi7P4JMkrOw,11114
|
9
9
|
ramifice/mixins.py,sha256=gKLmWQ-QrGO3K5_k-h1tDa08lkCql_dte2Jy05q1wsM,1125
|
10
|
-
ramifice/model.py,sha256=
|
10
|
+
ramifice/model.py,sha256=xhLKosxnT3HkPr6j_BSkB7pvG2WNY_7uylcHo3Oq0vM,6521
|
11
|
+
ramifice/pseudo_model.py,sha256=AdETZEvffFvHogtL0NYzp1rqLaI5lq3XcVX1n5v62bw,6816
|
11
12
|
ramifice/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
13
|
ramifice/store.py,sha256=MpDEPvUvbs11FXjakNtHPm9MekIv5p1U3as2Y80lTyc,1860
|
13
14
|
ramifice/translations.py,sha256=GNGE0ULAA0aOY5pTxUd3MQW-nVaKvp6BeXWEcsR0s0o,4048
|
@@ -38,10 +39,10 @@ ramifice/fields/color_field.py,sha256=sjVSoJU6TIZ66wzPx0WRl5zQsx-kINMMuxa_pSUpW6
|
|
38
39
|
ramifice/fields/date_field.py,sha256=Vlaxgp3WeDNMwbnQcTGu8kyQ7gc7M-7sFBPfzu-4MVw,5205
|
39
40
|
ramifice/fields/date_time_field.py,sha256=VpLeYJHs0df45fz4gnprtn7aipVFKGY4n02eRep7in0,5240
|
40
41
|
ramifice/fields/email_field.py,sha256=wOM07nQ5fOSOTydQhg3b4dV_95F1bd97peQ_XAnV0MY,3720
|
41
|
-
ramifice/fields/file_field.py,sha256=
|
42
|
+
ramifice/fields/file_field.py,sha256=NMM9k-kVes7VarxvTQmsl2f2nvRKp-upjPHMDjNIrwY,7892
|
42
43
|
ramifice/fields/float_field.py,sha256=4l7gzg46qQ7ZbXS6Z8GUhiUBR26rXChvO_uFx-lbUds,4555
|
43
44
|
ramifice/fields/id_field.py,sha256=5wlY2j051TB2GPsw0iQkFiQBV_Y_o1W_QnCtBlaDuYQ,4126
|
44
|
-
ramifice/fields/image_field.py,sha256=
|
45
|
+
ramifice/fields/image_field.py,sha256=u4YJ3vFtGj5uhVLjgrvV9gNLwLXT4jfVPUN2mlPKHfg,12269
|
45
46
|
ramifice/fields/integer_field.py,sha256=Gnm5IBSvWtCLGbg5pOSAdKa5xLzqXgiigVbzw8zcjas,4531
|
46
47
|
ramifice/fields/ip_field.py,sha256=D7xR3A-O049TQ_pEEKVTLsKF5cdr5O7ULyz4Pj3-cwQ,3601
|
47
48
|
ramifice/fields/password_field.py,sha256=5BlZZ4mXM0CAYDHguwsK8KFnvkOOmr8R0yowHByNVD4,3451
|
@@ -75,7 +76,7 @@ ramifice/paladins/groups/num_group.py,sha256=Jvb-lwHxapQybbLerC4t-_yO8N7Coo1fIlZ
|
|
75
76
|
ramifice/paladins/groups/pass_group.py,sha256=SEKpR2voNQtmywugDXJKY4XqPTL91CrJ87h0QNMqQqs,1952
|
76
77
|
ramifice/paladins/groups/slug_group.py,sha256=_IRil2PwpY7cH7WaExNksKz61kQjvc27blrEufgUB30,2323
|
77
78
|
ramifice/paladins/groups/text_group.py,sha256=nYZGwAIsJD-tX8RBtFlWvngO9RU4V0CnREUhxvV2UDo,3493
|
78
|
-
ramifice-0.3.
|
79
|
-
ramifice-0.3.
|
80
|
-
ramifice-0.3.
|
81
|
-
ramifice-0.3.
|
79
|
+
ramifice-0.3.21.dist-info/METADATA,sha256=4pWgH5EVC0dvdG2SGf4smhNUkvdXzbOEymF3YcYfvzQ,18904
|
80
|
+
ramifice-0.3.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
81
|
+
ramifice-0.3.21.dist-info/licenses/LICENSE,sha256=LrEL0aTZx90HDwFUQCJutORiDjJL9AnuVvCtspXIqt4,1095
|
82
|
+
ramifice-0.3.21.dist-info/RECORD,,
|
File without changes
|
File without changes
|