ramifice 0.8.2__py3-none-any.whl → 0.8.4__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.
@@ -1,17 +1,19 @@
1
1
  """Field of Model for upload file."""
2
2
 
3
- import os
4
3
  import uuid
5
4
  from base64 import b64decode
6
5
  from datetime import date
6
+ from os.path import basename
7
7
  from pathlib import Path
8
8
 
9
- import aiofiles
9
+ from aiofiles import open as async_open
10
+ from aiofiles import os as async_os
10
11
  from aioshutil import copyfile
11
12
 
12
13
  from ramifice.fields.general.field import Field
13
14
  from ramifice.fields.general.file_group import FileGroup
14
15
  from ramifice.utils import constants
16
+ from ramifice.utils.constants import MEDIA_ROOT, MEDIA_URL
15
17
  from ramifice.utils.errors import FileHasNoExtensionError
16
18
  from ramifice.utils.mixins.json_converter import JsonMixin
17
19
 
@@ -124,25 +126,25 @@ class FileField(Field, FileGroup, JsonMixin):
124
126
  # Create the current date for the directory name.
125
127
  date_str: str = str(date.today())
126
128
  # Create path to target directory.
127
- dir_target_path = f"{self.media_root}/{self.target_dir}/{date_str}"
129
+ dir_target_path = f"{MEDIA_ROOT}/uploads/{self.target_dir}/{date_str}"
128
130
  # Create target directory if it does not exist.
129
- if not await aiofiles.os.path.exists(dir_target_path):
130
- await aiofiles.os.makedirs(dir_target_path)
131
+ if not await async_os.path.exists(dir_target_path):
132
+ await async_os.makedirs(dir_target_path)
131
133
  # Create path to target file.
132
134
  f_target_path = f"{dir_target_path}/{f_uuid_name}"
133
135
  # Save file in target directory.
134
- async with aiofiles.open(f_target_path, mode="wb") as open_f:
136
+ async with async_open(f_target_path, mode="wb") as open_f:
135
137
  f_content = b64decode(base64_str)
136
138
  await open_f.write(f_content)
137
139
  # Add paths to target file.
138
140
  file_info["path"] = f_target_path
139
- file_info["url"] = f"{self.media_url}/{self.target_dir}/{date_str}/{f_uuid_name}"
141
+ file_info["url"] = f"{MEDIA_URL}/uploads/{self.target_dir}/{date_str}/{f_uuid_name}"
140
142
  # Add original file name.
141
143
  file_info["name"] = filename
142
144
  # Add file extension.
143
145
  file_info["extension"] = extension
144
146
  # Add file size (in bytes).
145
- file_info["size"] = await aiofiles.os.path.getsize(f_target_path)
147
+ file_info["size"] = await async_os.path.getsize(f_target_path)
146
148
  #
147
149
  # to value.
148
150
  self.value = file_info
@@ -169,23 +171,23 @@ class FileField(Field, FileGroup, JsonMixin):
169
171
  # Create the current date for the directory name.
170
172
  date_str: str = str(date.today())
171
173
  # Create path to target directory.
172
- dir_target_path = f"{self.media_root}/{self.target_dir}/{date_str}"
174
+ dir_target_path = f"{MEDIA_ROOT}/uploads/{self.target_dir}/{date_str}"
173
175
  # Create target directory if it does not exist.
174
- if not await aiofiles.os.path.exists(dir_target_path):
175
- await aiofiles.os.makedirs(dir_target_path)
176
+ if not await async_os.path.exists(dir_target_path):
177
+ await async_os.makedirs(dir_target_path)
176
178
  # Create path to target file.
177
179
  f_target_path = f"{dir_target_path}/{f_uuid_name}"
178
180
  # Save file in target directory.
179
181
  await copyfile(src_path, f_target_path)
180
182
  # Add paths to target file.
181
183
  file_info["path"] = f_target_path
182
- file_info["url"] = f"{self.media_url}/{self.target_dir}/{date_str}/{f_uuid_name}"
184
+ file_info["url"] = f"{MEDIA_URL}/uploads/{self.target_dir}/{date_str}/{f_uuid_name}"
183
185
  # Add original file name.
184
- file_info["name"] = os.path.basename(src_path)
186
+ file_info["name"] = basename(src_path)
185
187
  # Add file extension.
186
188
  file_info["extension"] = extension
187
189
  # Add file size (in bytes).
188
- file_info["size"] = await aiofiles.os.path.getsize(f_target_path)
190
+ file_info["size"] = await async_os.path.getsize(f_target_path)
189
191
  #
190
192
  # to value.
191
193
  self.value = file_info
@@ -31,5 +31,3 @@ class FileGroup(metaclass=ABCMeta):
31
31
  self.default = default
32
32
  self.target_dir = target_dir
33
33
  self.accept = accept
34
- self.media_root: str = "public/media/uploads"
35
- self.media_url: str = "/media/uploads"
@@ -1,17 +1,19 @@
1
1
  """Field of Model for upload image."""
2
2
 
3
- import os
4
3
  import uuid
5
4
  from base64 import b64decode
6
5
  from datetime import date
6
+ from os.path import basename
7
7
  from pathlib import Path
8
8
 
9
- import aiofiles
9
+ from aiofiles import open as async_open
10
+ from aiofiles import os as async_os
10
11
  from aioshutil import copyfile
11
12
 
12
13
  from ramifice.fields.general.field import Field
13
14
  from ramifice.fields.general.file_group import FileGroup
14
15
  from ramifice.utils import constants
16
+ from ramifice.utils.constants import MEDIA_ROOT, MEDIA_URL
15
17
  from ramifice.utils.errors import FileHasNoExtensionError
16
18
  from ramifice.utils.mixins.json_converter import JsonMixin
17
19
 
@@ -155,18 +157,18 @@ class ImageField(Field, FileGroup, JsonMixin):
155
157
  # Directory name for the original image and its thumbnails.
156
158
  general_dir = uuid.uuid4()
157
159
  # Create path to target directory with images.
158
- imgs_dir_path = f"{self.media_root}/{self.target_dir}/{date_str}/{general_dir}"
160
+ imgs_dir_path = f"{MEDIA_ROOT}/uploads/{self.target_dir}/{date_str}/{general_dir}"
159
161
  # Create url path to target directory with images.
160
- imgs_dir_url = f"{self.media_url}/{self.target_dir}/{date_str}/{general_dir}"
162
+ imgs_dir_url = f"{MEDIA_URL}/uploads/{self.target_dir}/{date_str}/{general_dir}"
161
163
  # Create a new name for the original image.
162
164
  new_original_name = f"original{extension}"
163
165
  # Create path to main image.
164
166
  main_img_path = f"{imgs_dir_path}/{new_original_name}"
165
167
  # Create target directory if it does not exist.
166
- if not await aiofiles.os.path.exists(imgs_dir_path):
167
- await aiofiles.os.makedirs(imgs_dir_path)
168
+ if not await async_os.path.exists(imgs_dir_path):
169
+ await async_os.makedirs(imgs_dir_path)
168
170
  # Save main image in target directory.
169
- async with aiofiles.open(main_img_path, mode="wb") as open_f:
171
+ async with async_open(main_img_path, mode="wb") as open_f:
170
172
  f_content = b64decode(base64_str)
171
173
  await open_f.write(f_content)
172
174
  # Add paths for main image.
@@ -186,7 +188,7 @@ class ImageField(Field, FileGroup, JsonMixin):
186
188
  # Add url path to target directory with images.
187
189
  img_info["imgs_dir_url"] = imgs_dir_url
188
190
  # Add size of main image (in bytes).
189
- img_info["size"] = await aiofiles.os.path.getsize(main_img_path)
191
+ img_info["size"] = await async_os.path.getsize(main_img_path)
190
192
  #
191
193
  # to value.
192
194
  self.value = img_info
@@ -213,23 +215,23 @@ class ImageField(Field, FileGroup, JsonMixin):
213
215
  # Directory name for the original image and its thumbnails.
214
216
  general_dir = uuid.uuid4()
215
217
  # Create path to target directory with images.
216
- imgs_dir_path = f"{self.media_root}/{self.target_dir}/{date_str}/{general_dir}"
218
+ imgs_dir_path = f"{MEDIA_ROOT}/uploads/{self.target_dir}/{date_str}/{general_dir}"
217
219
  # Create url path to target directory with images.
218
- imgs_dir_url = f"{self.media_url}/{self.target_dir}/{date_str}/{general_dir}"
220
+ imgs_dir_url = f"{MEDIA_URL}/uploads/{self.target_dir}/{date_str}/{general_dir}"
219
221
  # Create a new name for the original image.
220
222
  new_original_name = f"original{extension}"
221
223
  # Create path to main image.
222
224
  main_img_path = f"{imgs_dir_path}/{new_original_name}"
223
225
  # Create target directory if it does not exist.
224
- if not await aiofiles.os.path.exists(imgs_dir_path):
225
- await aiofiles.os.makedirs(imgs_dir_path)
226
+ if not await async_os.path.exists(imgs_dir_path):
227
+ await async_os.makedirs(imgs_dir_path)
226
228
  # Save main image in target directory.
227
229
  await copyfile(src_path, main_img_path)
228
230
  # Add paths for main image.
229
231
  img_info["path"] = main_img_path
230
232
  img_info["url"] = f"{imgs_dir_url}/{new_original_name}"
231
233
  # Add original image name.
232
- img_info["name"] = os.path.basename(src_path)
234
+ img_info["name"] = basename(src_path)
233
235
  # Add image extension.
234
236
  img_info["extension"] = extension
235
237
  # Transform extension to the upper register and delete the point.
@@ -242,7 +244,7 @@ class ImageField(Field, FileGroup, JsonMixin):
242
244
  # Add url path to target directory with images.
243
245
  img_info["imgs_dir_url"] = imgs_dir_url
244
246
  # Add size of main image (in bytes).
245
- img_info["size"] = await aiofiles.os.path.getsize(main_img_path)
247
+ img_info["size"] = await async_os.path.getsize(main_img_path)
246
248
  #
247
249
  # to value.
248
250
  self.value = img_info
@@ -42,6 +42,7 @@ def model(
42
42
  raise DoesNotMatchRegexError("^[A-Z][a-zA-Z0-9]{0,24}$")
43
43
  if fixture_name is not None:
44
44
  fixture_path = f"config/fixtures/{fixture_name}.yml"
45
+
45
46
  if not os.path.exists(fixture_path):
46
47
  msg = (
47
48
  f"Model: `{cls.__module__}.{cls.__name__}` > "
@@ -2,7 +2,7 @@
2
2
 
3
3
  from typing import Any
4
4
 
5
- from aiofiles import os
5
+ from aiofiles import os as async_os
6
6
  from aioshutil import rmtree
7
7
  from bson.objectid import ObjectId
8
8
  from pymongo.asynchronous.collection import AsyncCollection
@@ -133,7 +133,7 @@ class CheckMixin(
133
133
  file_data = result_map.get(field_name)
134
134
  if file_data is not None:
135
135
  if file_data["is_new_file"]:
136
- await os.remove(file_data["path"])
136
+ await async_os.remove(file_data["path"])
137
137
  field_data.value = None
138
138
  if curr_doc is not None:
139
139
  field_data.value = curr_doc[field_name]
@@ -2,7 +2,7 @@
2
2
 
3
3
  from typing import Any
4
4
 
5
- from aiofiles import os
5
+ from aiofiles import os as async_os
6
6
  from aioshutil import rmtree
7
7
  from pymongo.asynchronous.collection import AsyncCollection
8
8
 
@@ -77,7 +77,7 @@ class DeleteMixin:
77
77
  if group == "file":
78
78
  file_data = mongo_doc[field_name]
79
79
  if file_data is not None and len(file_data["path"]) > 0:
80
- await os.remove(file_data["path"])
80
+ await async_os.remove(file_data["path"])
81
81
  file_data = None
82
82
  elif group == "img":
83
83
  file_data = mongo_doc[field_name]
@@ -7,9 +7,11 @@ List of variables:
7
7
  - `MONGO_DATABASE` - Caching a Mongo database.
8
8
  - `DATABASE_NAME` - Caching a database name.
9
9
  - `SUPER_COLLECTION_NAME` - Caching a super collection name.
10
+ - `MEDIA_ROOT` - Absolute filesystem path to the directory that will hold user-uploaded files.
11
+ - `MEDIA_URL` - URL that handles the media served from MEDIA_ROOT, used for managing stored files.
12
+ - `STATIC_ROOT` - The absolute path to the directory where static files are located.
13
+ - `STATIC_URL` - URL to use when referring to static files located in STATIC_ROOT.
10
14
  - `REGEX` - Caching a patterns of regular expression.
11
- - `FILE_INFO_DICT` - Caching a dictionary to transmit information about the file.
12
- - `IMG_INFO_DICT` - Caching a dictionary to transmit information about the image.
13
15
  """
14
16
 
15
17
  import re
@@ -30,6 +32,18 @@ DATABASE_NAME: str | None = None
30
32
  # Store technical data for Models migration into a database.
31
33
  # Store dynamic field data for simulate relationship Many-to-One and Many-to-Manyю.
32
34
  SUPER_COLLECTION_NAME: str = "SUPER_COLLECTION"
35
+ # Absolute filesystem path to the
36
+ # directory that will hold user-uploaded files.
37
+ MEDIA_ROOT: str = "public/media"
38
+ # URL that handles the media served from MEDIA_ROOT,
39
+ # used for managing stored files.
40
+ MEDIA_URL: str = "/media"
41
+ # The absolute path to the
42
+ # directory where static files are located.
43
+ STATIC_ROOT: str = "public/static"
44
+ # URL to use when referring to
45
+ # static files located in STATIC_ROOT.
46
+ STATIC_URL: str = "/static"
33
47
  # Caching a patterns of regular expression.
34
48
  REGEX: dict[str, re.Pattern] = {
35
49
  "database_name": re.compile(r"^[a-zA-Z][-_a-zA-Z0-9]{0,59}$"),
ramifice/utils/tools.py CHANGED
@@ -3,11 +3,11 @@
3
3
  import asyncio
4
4
  import ipaddress
5
5
  import math
6
- import os
7
6
  from typing import Any
8
7
  from urllib.parse import urlparse
9
8
 
10
9
  import phonenumbers
10
+ from aiofiles import ospath
11
11
  from bson.objectid import ObjectId
12
12
  from email_validator import EmailNotValidError, validate_email
13
13
 
@@ -29,9 +29,9 @@ def to_human_size(size: int) -> str:
29
29
  return f"{size} {order}"
30
30
 
31
31
 
32
- def get_file_size(path: str) -> int:
32
+ async def get_file_size(path: str) -> int:
33
33
  """Get file size in bytes."""
34
- size: int = os.path.getsize(path)
34
+ size: int = await ospath.getsize(path)
35
35
  return size
36
36
 
37
37
 
@@ -30,11 +30,11 @@ from typing import Any
30
30
 
31
31
  from ramifice.utils.errors import PanicError
32
32
 
33
- # Language code by default.
33
+ # Language by default.
34
34
  DEFAULT_LOCALE: str = "en"
35
35
  # Code of current language.
36
36
  CURRENT_LOCALE: str = copy.deepcopy(DEFAULT_LOCALE)
37
- # List of codes supported by languages.
37
+ # List of supported languages.
38
38
  LANGUAGES: frozenset[str] = frozenset(("en", "ru"))
39
39
 
40
40
  if not DEFAULT_LOCALE in LANGUAGES:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ramifice
3
- Version: 0.8.2
3
+ Version: 0.8.4
4
4
  Summary: ORM-like API MongoDB for Python language.
5
5
  Project-URL: Homepage, https://github.com/kebasyaty/ramifice
6
6
  Project-URL: Documentation, https://kebasyaty.github.io/ramifice/
@@ -25,10 +25,10 @@ ramifice/fields/color_field.py,sha256=K-lYp8f08HTZt6WU0kY3CQWaAmx6I3-uyKHBq7xAeS
25
25
  ramifice/fields/date_field.py,sha256=-M_dD8tVUXcDCQD5Hy5Ns9Z2QGlRya0qbvoWgA4qpmg,5277
26
26
  ramifice/fields/date_time_field.py,sha256=ahjX-cD5Qj-V7JUi5QAZ8qgv9UePhYYYzJghiasKdqE,5312
27
27
  ramifice/fields/email_field.py,sha256=4Kxy1K6CXJUJ8Db0CQuopjodiY4ybe7V0GT756t7Nks,3481
28
- ramifice/fields/file_field.py,sha256=icckL_LGfPlotHO7j32lMre9J0O0k-WVjMQg5QdQav4,8130
28
+ ramifice/fields/file_field.py,sha256=CSCHSExyXUsvLGGCj3WmBHM0YUUcK9QZZb3kB8r8HQs,8253
29
29
  ramifice/fields/float_field.py,sha256=JeVwoIi6j-dovXarZ3NgmSQI7f3qZzydlRs_yqp3Faw,4637
30
30
  ramifice/fields/id_field.py,sha256=YO5oIwXtqgURjjd2oYe2ouXxdpaiJp5ZhUgUA2pkNeo,4183
31
- ramifice/fields/image_field.py,sha256=qoUAkZW2b-EkU5IDvlbxEOFwWpLYFXs9ZFnxL55an6M,11549
31
+ ramifice/fields/image_field.py,sha256=ZBbraEI0P5qWK1_xDnZ42YXhqgK4LdcL28vg9zgPBiw,11672
32
32
  ramifice/fields/integer_field.py,sha256=xPwpdhYNz4MyHgOypqa29oGNdKZic05yyUYHg5CDD3E,4609
33
33
  ramifice/fields/ip_field.py,sha256=tBFGFMC_zwAyLGtVRNNfiu4vQoxXPcgVHhX_aoHrvns,3390
34
34
  ramifice/fields/password_field.py,sha256=USD4Y8MTI89QZVT6zqLGBmb5kqOKrXqh73-z99cOvtk,3266
@@ -40,15 +40,15 @@ ramifice/fields/general/__init__.py,sha256=JzgIDseNQLB_IDwY9-JSiT_bONxvhJmbKl5Ph
40
40
  ramifice/fields/general/choice_group.py,sha256=aNO8LnsafGOOQ7d2lAppI9YtzVHbWUZ6t1znEEIbAv8,962
41
41
  ramifice/fields/general/date_group.py,sha256=C5VgR0CBEGgG7nIYEeI82hVXttngSJAGFvEdXC_OpGY,1213
42
42
  ramifice/fields/general/field.py,sha256=qm7N-U5H1V6BK_6quJeGGpTmb9IqA-vxZlNHwoABye0,1384
43
- ramifice/fields/general/file_group.py,sha256=T-IwFnlJAWaRcQKVn09IJCdFNGgnUfMkjLzWFFqn7gY,1117
43
+ ramifice/fields/general/file_group.py,sha256=pC9bGUpDeu7FLJVD6NMCzq18aPETotipknSly2GH7Q0,1014
44
44
  ramifice/fields/general/number_group.py,sha256=LOPHbKAnIuqW4DEh2fH78w6qOQjp0OhkuP7MoikhlkA,761
45
45
  ramifice/fields/general/text_group.py,sha256=m9EnlYGwqlck-JIYOCUd0hUYAVr8jH4YCnTmm9QlCkQ,1103
46
46
  ramifice/models/__init__.py,sha256=h_QQ5rSJNZ-7kmx7wIFd8E8RmUS94b_x4jdwMbq8zm4,15
47
- ramifice/models/decorator.py,sha256=fL9xc7-_BEiFEqfwgql6CihYWB6MC-JgiC0FSSK_JHA,5956
47
+ ramifice/models/decorator.py,sha256=dzwC489KBjamQxrWNy8lrdXVLb3e8OKPWbPGJZqxaFA,5958
48
48
  ramifice/models/model.py,sha256=e5UWHKIHwm7saxQ4bbamsTkz1VVBIqTRlAmJCOg5uRE,7160
49
49
  ramifice/paladins/__init__.py,sha256=EJ6EEKXUcMG5P1bDmlEa4W3fsxSz5u6mzDG_oU1efMQ,618
50
- ramifice/paladins/check.py,sha256=9lLJ6SnNQBI9eyAwWSqjFa7_F2A2QbTg4WHyrPL3E20,6584
51
- ramifice/paladins/delete.py,sha256=90ZiTzYxa2bnhOJhkEBe7XR_Yi4gaTvrJ3u4dSik0mU,3430
50
+ ramifice/paladins/check.py,sha256=c-7jbFV5qzE9nF8Cl6VSPA8DtziADOuV6j1rldnZBKI,6602
51
+ ramifice/paladins/delete.py,sha256=teUyj-sosObatoaFpMC3wodC4GvWbzgJCCxsI_Z9vyk,3448
52
52
  ramifice/paladins/password.py,sha256=G-tMz2dIiOYEhOeSHYgAmc2e4tG_TJ2gt5tzPwBapkw,3047
53
53
  ramifice/paladins/refrash.py,sha256=hIZQEBGtoIhuGGg2KQYGApCaMb8WhK_QZlzi1_-zzU0,1100
54
54
  ramifice/paladins/save.py,sha256=mFau65uWYy4dSVyTVdZGR3VUnP-I_NPdMvq7F5n1sFo,3899
@@ -66,19 +66,19 @@ ramifice/paladins/groups/pass_group.py,sha256=uuIIqMBXsYG7vTHc_AhdgWuNCivxTgQMjk
66
66
  ramifice/paladins/groups/slug_group.py,sha256=ztiA2v7e5lQYRZtlLw8WGOhSWaYQfOdZ6wkKbx3ZbTM,2329
67
67
  ramifice/paladins/groups/text_group.py,sha256=cdwUZoomw61A7CmDIbds_oOaia2tD-J5LLuj5b8E8O4,4472
68
68
  ramifice/utils/__init__.py,sha256=xixHoOX4ja5jIUZemem1qn4k4aonv3G3Q76azQK_pkU,43
69
- ramifice/utils/constants.py,sha256=uR20um3Qg_1SG1t7WyWbpq8kQD-9Mslyr_c1yh5Hw9w,1751
69
+ ramifice/utils/constants.py,sha256=dTSk07FVc6hgXwG1IF0_klPnbfxlFHiaRmym5zWgzIM,2395
70
70
  ramifice/utils/errors.py,sha256=iuhq7fzpUmsOyeXeg2fJjta8yAuqlXLKsZVMpfUhtHE,1901
71
71
  ramifice/utils/fixtures.py,sha256=PMRl1Ud0Ek0eX5F7BPiL7az_H7YKtIVKB8MJ17CeBdw,3179
72
72
  ramifice/utils/migration.py,sha256=5VYYB7DFMzLCRYWdlwtqGRSUPFdXp5SMxuMWraofVCw,11106
73
- ramifice/utils/tools.py,sha256=dJjG92nevSq_US30pUdbBy37pvqBdxXHBeHZxpOInKM,2843
74
- ramifice/utils/translations.py,sha256=aEkNKilD7RlJjBixqhh0cfIMw9lg13woaIwd3dyR8G4,4247
73
+ ramifice/utils/tools.py,sha256=DSSR__sHgfDlYF4vhtR8vmeFtgquonkM5W_3V3SX6Y0,2872
74
+ ramifice/utils/translations.py,sha256=Ibx6HqPcnsNxuBfXDs9mCLEA7xu4mkl3q39vqFaK-GI,4233
75
75
  ramifice/utils/unit.py,sha256=PPNKWYFJ8cz0nwbBPaTdL58_Nr7N0fIHFJBpKG2ZLKI,2482
76
76
  ramifice/utils/mixins/__init__.py,sha256=GrxJDsw73bEkitIh0-0BCxNnUK-N5uRXMCRlaPoaz1o,265
77
77
  ramifice/utils/mixins/add_valid.py,sha256=TLOObedzXNA9eCylfAVbVCqIKE5sV-P5AdIN7avj-GA,407
78
78
  ramifice/utils/mixins/hooks.py,sha256=33jvJRhfnJeL2Hd_YFXk3M_7wjqHaByU2wRjKyboL6s,914
79
79
  ramifice/utils/mixins/indexing.py,sha256=Z0427HoaVRyNmSNN8Fx0mSICgAKV-gDdu3iR5qYUEbs,329
80
80
  ramifice/utils/mixins/json_converter.py,sha256=2K_PZ34AzpesusgyyQFOLXFPcjXez3TWn-m9CHIKwRo,1131
81
- ramifice-0.8.2.dist-info/METADATA,sha256=GJe52VrqCxHD3lZ3UqDtg01-cRnwjC6UumDJxieVlR4,21016
82
- ramifice-0.8.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
83
- ramifice-0.8.2.dist-info/licenses/LICENSE,sha256=LrEL0aTZx90HDwFUQCJutORiDjJL9AnuVvCtspXIqt4,1095
84
- ramifice-0.8.2.dist-info/RECORD,,
81
+ ramifice-0.8.4.dist-info/METADATA,sha256=D7LCsTfcUITuLa2XzN_DS5sSgED6Scv9JfaXcu5b84k,21016
82
+ ramifice-0.8.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
83
+ ramifice-0.8.4.dist-info/licenses/LICENSE,sha256=LrEL0aTZx90HDwFUQCJutORiDjJL9AnuVvCtspXIqt4,1095
84
+ ramifice-0.8.4.dist-info/RECORD,,