lamindb_setup 1.7.3__py3-none-any.whl → 1.8.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.
- lamindb_setup/__init__.py +1 -1
- lamindb_setup/_set_managed_storage.py +17 -4
- lamindb_setup/core/_settings_instance.py +60 -33
- lamindb_setup/core/_settings_storage.py +31 -8
- lamindb_setup/core/upath.py +7 -3
- {lamindb_setup-1.7.3.dist-info → lamindb_setup-1.8.0.dist-info}/METADATA +1 -1
- {lamindb_setup-1.7.3.dist-info → lamindb_setup-1.8.0.dist-info}/RECORD +9 -9
- {lamindb_setup-1.7.3.dist-info → lamindb_setup-1.8.0.dist-info}/LICENSE +0 -0
- {lamindb_setup-1.7.3.dist-info → lamindb_setup-1.8.0.dist-info}/WHEEL +0 -0
lamindb_setup/__init__.py
CHANGED
|
@@ -7,17 +7,19 @@ from lamin_utils import logger
|
|
|
7
7
|
from ._init_instance import register_storage_in_instance
|
|
8
8
|
from .core._hub_core import delete_storage_record
|
|
9
9
|
from .core._settings import settings
|
|
10
|
-
from .core._settings_storage import init_storage
|
|
10
|
+
from .core._settings_storage import StorageSettings, init_storage
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from lamindb_setup.types import UPathStr
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def set_managed_storage(root: UPathStr, **fs_kwargs):
|
|
16
|
+
def set_managed_storage(root: UPathStr, host: str | None = None, **fs_kwargs):
|
|
17
17
|
"""Add or switch to another managed storage location.
|
|
18
18
|
|
|
19
19
|
Args:
|
|
20
20
|
root: `UPathStr` - The new storage root, e.g., an S3 bucket.
|
|
21
|
+
host: `str | None = None` For a shared local storage location, pass a globally unique host identifier, e.g. `"my-institute-cluster-1"`, `"my-server-abcd"`, ...
|
|
22
|
+
Discuss the naming convention with an admin.
|
|
21
23
|
**fs_kwargs: Additional fsspec arguments for cloud root, e.g., profile.
|
|
22
24
|
|
|
23
25
|
"""
|
|
@@ -29,6 +31,16 @@ def set_managed_storage(root: UPathStr, **fs_kwargs):
|
|
|
29
31
|
raise ValueError(
|
|
30
32
|
"Can't add additional managed storage locations for instances that aren't managed through the hub."
|
|
31
33
|
)
|
|
34
|
+
|
|
35
|
+
# we do not just query the instance storage table because
|
|
36
|
+
# we might need some information from the hub
|
|
37
|
+
if not StorageSettings(root).type_is_cloud and host is None:
|
|
38
|
+
host = "unspecified-host"
|
|
39
|
+
logger.warning(
|
|
40
|
+
"setting local storage locations with a single path is deprecated, "
|
|
41
|
+
"use a tuple of (local_root, host) instead"
|
|
42
|
+
)
|
|
43
|
+
|
|
32
44
|
# here the storage is registered in the hub
|
|
33
45
|
# hub_record_status="hub-record-created" if a new record is created
|
|
34
46
|
# "hub-record-retrieved" if the storage is in the hub already
|
|
@@ -36,13 +48,14 @@ def set_managed_storage(root: UPathStr, **fs_kwargs):
|
|
|
36
48
|
root=root,
|
|
37
49
|
instance_id=settings.instance._id,
|
|
38
50
|
instance_slug=settings.instance.slug,
|
|
39
|
-
register_hub=
|
|
51
|
+
register_hub=settings.instance.is_on_hub,
|
|
52
|
+
prevent_register_hub=not settings.instance.is_on_hub,
|
|
53
|
+
region=host,
|
|
40
54
|
)
|
|
41
55
|
if ssettings._instance_id is None:
|
|
42
56
|
raise ValueError(
|
|
43
57
|
f"Cannot manage storage without write access: {ssettings.root}"
|
|
44
58
|
)
|
|
45
|
-
|
|
46
59
|
# here the storage is saved in the instance
|
|
47
60
|
# if any error happens the record in the hub is deleted
|
|
48
61
|
# if it was created earlier and not retrieved
|
|
@@ -32,6 +32,8 @@ if TYPE_CHECKING:
|
|
|
32
32
|
|
|
33
33
|
from ._settings_user import UserSettings
|
|
34
34
|
|
|
35
|
+
LOCAL_STORAGE_MESSAGE = "No storage location found in current environment: create one via, e.g., ln.Storage(root='/dir/our_shared_dir', host='our-server-123).save()"
|
|
36
|
+
|
|
35
37
|
|
|
36
38
|
def sanitize_git_repo_url(repo_url: str) -> str:
|
|
37
39
|
assert repo_url.startswith("https://")
|
|
@@ -82,7 +84,7 @@ class InstanceSettings:
|
|
|
82
84
|
self._git_repo = None if git_repo is None else sanitize_git_repo_url(git_repo)
|
|
83
85
|
# local storage
|
|
84
86
|
self._keep_artifacts_local = keep_artifacts_local
|
|
85
|
-
self.
|
|
87
|
+
self._local_storage: StorageSettings | None = None
|
|
86
88
|
self._is_on_hub = is_on_hub
|
|
87
89
|
# private, needed for api requests
|
|
88
90
|
self._api_url = api_url
|
|
@@ -151,7 +153,7 @@ class InstanceSettings:
|
|
|
151
153
|
except ProgrammingError:
|
|
152
154
|
logger.error("not able to load Storage registry: please migrate")
|
|
153
155
|
return None
|
|
154
|
-
found =
|
|
156
|
+
found = []
|
|
155
157
|
for record in all_local_records:
|
|
156
158
|
root_path = Path(record.root)
|
|
157
159
|
if root_path.exists():
|
|
@@ -175,18 +177,16 @@ class InstanceSettings:
|
|
|
175
177
|
)
|
|
176
178
|
continue
|
|
177
179
|
if uid == record.uid:
|
|
178
|
-
found
|
|
179
|
-
break
|
|
180
|
+
found.append(record)
|
|
180
181
|
if found:
|
|
182
|
+
if len(found) > 1:
|
|
183
|
+
found_display = "\n - ".join([f"{record.root}" for record in found])
|
|
184
|
+
logger.important(f"found locations:\n - {found_display}")
|
|
185
|
+
logger.important(f"defaulting to local storage: {record.root}")
|
|
181
186
|
return StorageSettings(record.root)
|
|
182
187
|
elif not mute_warning:
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
+ "\n ".join(r.root for r in all_local_records)
|
|
186
|
-
)
|
|
187
|
-
logger.important(
|
|
188
|
-
"please register a new local storage location via `ln.settings.storage_local = local_root_path` and re-load/connect the instance"
|
|
189
|
-
)
|
|
188
|
+
start = LOCAL_STORAGE_MESSAGE[0].lower()
|
|
189
|
+
logger.warning(f"{start}{LOCAL_STORAGE_MESSAGE[1:]}")
|
|
190
190
|
return None
|
|
191
191
|
|
|
192
192
|
@property
|
|
@@ -209,8 +209,8 @@ class InstanceSettings:
|
|
|
209
209
|
return self._storage
|
|
210
210
|
|
|
211
211
|
@property
|
|
212
|
-
def
|
|
213
|
-
"""An additional local
|
|
212
|
+
def local_storage(self) -> StorageSettings:
|
|
213
|
+
"""An additional local storage location.
|
|
214
214
|
|
|
215
215
|
Is only available if :attr:`keep_artifacts_local` is enabled.
|
|
216
216
|
|
|
@@ -218,46 +218,73 @@ class InstanceSettings:
|
|
|
218
218
|
"""
|
|
219
219
|
if not self._keep_artifacts_local:
|
|
220
220
|
raise ValueError("`keep_artifacts_local` is not enabled for this instance.")
|
|
221
|
-
if self.
|
|
222
|
-
self.
|
|
223
|
-
if self.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
def storage_local(self, local_root: Path | str):
|
|
221
|
+
if self._local_storage is None:
|
|
222
|
+
self._local_storage = self._search_local_root()
|
|
223
|
+
if self._local_storage is None:
|
|
224
|
+
raise ValueError(LOCAL_STORAGE_MESSAGE)
|
|
225
|
+
return self._local_storage
|
|
226
|
+
|
|
227
|
+
@local_storage.setter
|
|
228
|
+
def local_storage(self, local_root_host: tuple[Path | str, str]):
|
|
230
229
|
from lamindb_setup._init_instance import register_storage_in_instance
|
|
231
230
|
|
|
231
|
+
if not isinstance(local_root_host, tuple):
|
|
232
|
+
local_root = local_root_host
|
|
233
|
+
host = "unspecified-host"
|
|
234
|
+
else:
|
|
235
|
+
local_root, host = local_root_host
|
|
236
|
+
|
|
232
237
|
local_root = Path(local_root)
|
|
233
238
|
if not self._keep_artifacts_local:
|
|
234
239
|
raise ValueError("`keep_artifacts_local` is not enabled for this instance.")
|
|
235
|
-
|
|
240
|
+
local_storage = self._search_local_root(
|
|
236
241
|
local_root=StorageSettings(local_root).root_as_str, mute_warning=True
|
|
237
242
|
)
|
|
238
|
-
if
|
|
243
|
+
if local_storage is not None:
|
|
239
244
|
# great, we're merely switching storage location
|
|
240
|
-
self.
|
|
241
|
-
logger.important(f"defaulting to local storage: {storage_local.root}")
|
|
245
|
+
self._local_storage = local_storage
|
|
242
246
|
return None
|
|
243
|
-
|
|
244
|
-
if
|
|
247
|
+
local_storage = self._search_local_root(mute_warning=True)
|
|
248
|
+
if local_storage is not None:
|
|
245
249
|
if os.getenv("LAMIN_TESTING") == "true":
|
|
246
250
|
response = "y"
|
|
247
251
|
else:
|
|
248
252
|
response = input(
|
|
249
253
|
"You already configured a local storage root for this instance in this"
|
|
250
|
-
f" environment: {self.
|
|
254
|
+
f" environment: {self.local_storage.root}\nDo you want to register another one? (y/n)"
|
|
251
255
|
)
|
|
252
256
|
if response != "y":
|
|
253
257
|
return None
|
|
258
|
+
if host == "unspecified-host":
|
|
259
|
+
logger.warning(
|
|
260
|
+
"setting local_storage with a single path is deprecated for creating storage locations"
|
|
261
|
+
)
|
|
262
|
+
logger.warning(
|
|
263
|
+
"use this instead: ln.Storage(root='/dir/our_shared_dir', host='our-server-123').save()"
|
|
264
|
+
)
|
|
254
265
|
local_root = UPath(local_root)
|
|
255
266
|
assert isinstance(local_root, LocalPathClasses)
|
|
256
|
-
self.
|
|
257
|
-
local_root,
|
|
267
|
+
self._local_storage, _ = init_storage(
|
|
268
|
+
local_root,
|
|
269
|
+
instance_id=self._id,
|
|
270
|
+
instance_slug=self.slug,
|
|
271
|
+
register_hub=True,
|
|
272
|
+
region=host,
|
|
258
273
|
) # type: ignore
|
|
259
|
-
register_storage_in_instance(self.
|
|
260
|
-
logger.important(
|
|
274
|
+
register_storage_in_instance(self._local_storage) # type: ignore
|
|
275
|
+
logger.important(
|
|
276
|
+
f"defaulting to local storage: {self._local_storage.root} on host {host}"
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
@property
|
|
280
|
+
@deprecated("local_storage")
|
|
281
|
+
def storage_local(self) -> StorageSettings:
|
|
282
|
+
return self.local_storage
|
|
283
|
+
|
|
284
|
+
@storage_local.setter
|
|
285
|
+
@deprecated("local_storage")
|
|
286
|
+
def storage_local(self, local_root_host: tuple[Path | str, str]):
|
|
287
|
+
self.local_storage = local_root_host # type: ignore
|
|
261
288
|
|
|
262
289
|
@property
|
|
263
290
|
def slug(self) -> str:
|
|
@@ -87,6 +87,15 @@ def get_storage_region(path: UPathStr) -> str | None:
|
|
|
87
87
|
return region
|
|
88
88
|
|
|
89
89
|
|
|
90
|
+
def get_storage_type(root_as_str: str) -> StorageType:
|
|
91
|
+
import fsspec
|
|
92
|
+
|
|
93
|
+
convert = {"file": "local"}
|
|
94
|
+
# init_storage checks that the root protocol belongs to VALID_PROTOCOLS
|
|
95
|
+
protocol = fsspec.utils.get_protocol(root_as_str)
|
|
96
|
+
return convert.get(protocol, protocol) # type: ignore
|
|
97
|
+
|
|
98
|
+
|
|
90
99
|
def mark_storage_root(
|
|
91
100
|
root: UPathStr, uid: str, instance_id: UUID, instance_slug: str
|
|
92
101
|
) -> Literal["__marked__"] | str:
|
|
@@ -125,6 +134,7 @@ def init_storage(
|
|
|
125
134
|
init_instance: bool = False,
|
|
126
135
|
created_by: UUID | None = None,
|
|
127
136
|
access_token: str | None = None,
|
|
137
|
+
region: str | None = None,
|
|
128
138
|
) -> tuple[
|
|
129
139
|
StorageSettings,
|
|
130
140
|
Literal["hub-record-not-created", "hub-record-retrieved", "hub-record-created"],
|
|
@@ -145,7 +155,6 @@ def init_storage(
|
|
|
145
155
|
# this means we constructed a hosted location of shape s3://bucket-name/uid
|
|
146
156
|
# within LaminHub
|
|
147
157
|
assert root_str.endswith(uid)
|
|
148
|
-
region = None
|
|
149
158
|
lamin_env = os.getenv("LAMIN_ENV")
|
|
150
159
|
if root_str.startswith("create-s3"):
|
|
151
160
|
if root_str != "create-s3":
|
|
@@ -177,6 +186,10 @@ def init_storage(
|
|
|
177
186
|
register_hub = (
|
|
178
187
|
register_hub or ssettings.type_is_cloud
|
|
179
188
|
) # default to registering cloud storage
|
|
189
|
+
if register_hub and not ssettings.type_is_cloud and ssettings.host is None:
|
|
190
|
+
raise ValueError(
|
|
191
|
+
"`host` must be set for local storage locations that are registered on the hub"
|
|
192
|
+
)
|
|
180
193
|
hub_record_status = init_storage_hub(
|
|
181
194
|
ssettings,
|
|
182
195
|
auto_populate_instance=not init_instance,
|
|
@@ -226,7 +239,10 @@ def init_storage(
|
|
|
226
239
|
|
|
227
240
|
|
|
228
241
|
class StorageSettings:
|
|
229
|
-
"""Settings for a storage location (local or cloud).
|
|
242
|
+
"""Settings for a storage location (local or cloud).
|
|
243
|
+
|
|
244
|
+
Do not instantiate this class yourself, use `ln.Storage` instead.
|
|
245
|
+
"""
|
|
230
246
|
|
|
231
247
|
def __init__(
|
|
232
248
|
self,
|
|
@@ -385,6 +401,18 @@ class StorageSettings:
|
|
|
385
401
|
"""`True` if `storage_root` is in cloud, `False` otherwise."""
|
|
386
402
|
return self.type != "local"
|
|
387
403
|
|
|
404
|
+
@property
|
|
405
|
+
def host(self) -> str | None:
|
|
406
|
+
"""Host identifier for local storage locations.
|
|
407
|
+
|
|
408
|
+
Is `None` for locations with `type != "local"`.
|
|
409
|
+
|
|
410
|
+
A globally unique user-defined host identifier (cluster, server, laptop, etc.).
|
|
411
|
+
"""
|
|
412
|
+
if self.type != "local":
|
|
413
|
+
return None
|
|
414
|
+
return self.region
|
|
415
|
+
|
|
388
416
|
@property
|
|
389
417
|
def region(self) -> str | None:
|
|
390
418
|
"""Storage region."""
|
|
@@ -398,12 +426,7 @@ class StorageSettings:
|
|
|
398
426
|
|
|
399
427
|
Returns the protocol as a stringe, e.g., "local", "s3", "gs", "http", "https".
|
|
400
428
|
"""
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
convert = {"file": "local"}
|
|
404
|
-
# init_storage checks that the root protocol belongs to VALID_PROTOCOLS
|
|
405
|
-
protocol = fsspec.utils.get_protocol(self.root_as_str)
|
|
406
|
-
return convert.get(protocol, protocol) # type: ignore
|
|
429
|
+
return get_storage_type(self.root_as_str)
|
|
407
430
|
|
|
408
431
|
@property
|
|
409
432
|
def is_on_hub(self) -> bool:
|
lamindb_setup/core/upath.py
CHANGED
|
@@ -928,9 +928,13 @@ def check_storage_is_empty(
|
|
|
928
928
|
# if the storage_uid.txt was somehow deleted, we restore a dummy version of it
|
|
929
929
|
# because we need it to count files in an empty directory on S3 (otherwise permission error)
|
|
930
930
|
if not (root_upath / STORAGE_UID_FILE_KEY).exists():
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
931
|
+
try:
|
|
932
|
+
(root_upath / STORAGE_UID_FILE_KEY).write_text(
|
|
933
|
+
"was deleted, restored during delete"
|
|
934
|
+
)
|
|
935
|
+
except FileNotFoundError:
|
|
936
|
+
# this can happen if the root is a local non-existing path
|
|
937
|
+
pass
|
|
934
938
|
if account_for_sqlite_file:
|
|
935
939
|
n_offset_objects += 1 # the SQLite file is in the ".lamindb" directory
|
|
936
940
|
if root_string.startswith(HOSTED_BUCKETS):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
lamindb_setup/__init__.py,sha256=
|
|
1
|
+
lamindb_setup/__init__.py,sha256=t86vewd7XZhz-qY1WJCxdsaTAqsjwQXiMF7Y6L2odJQ,2782
|
|
2
2
|
lamindb_setup/_cache.py,sha256=5o749NuW6zi6uP4rmBtwxg7ifWpAHXVngzC0tEgXLgo,2776
|
|
3
3
|
lamindb_setup/_check.py,sha256=28PcG8Kp6OpjSLSi1r2boL2Ryeh6xkaCL87HFbjs6GA,129
|
|
4
4
|
lamindb_setup/_check_setup.py,sha256=bXuqx2HEc178RM7gbKZQ65PEVJFu6uSOKiHAs_xz6GI,5575
|
|
@@ -14,7 +14,7 @@ lamindb_setup/_migrate.py,sha256=ya-15sc91i4JmEWI4j00T2892x8hdy2fSW-qz4IdxLs,973
|
|
|
14
14
|
lamindb_setup/_register_instance.py,sha256=zmk7UrOF6qED_zTEaTM8GbZurMSP1SwddkRy7rpYmUY,1215
|
|
15
15
|
lamindb_setup/_schema.py,sha256=b3uzhhWpV5mQtDwhMINc2MabGCnGLESy51ito3yl6Wc,679
|
|
16
16
|
lamindb_setup/_schema_metadata.py,sha256=yMSuLZc-7iWUV7tETNuKmcDeMW0wtUr5ypKxsKC-m7k,14898
|
|
17
|
-
lamindb_setup/_set_managed_storage.py,sha256=
|
|
17
|
+
lamindb_setup/_set_managed_storage.py,sha256=2muJxbjhZTSgMcyLuLS0PyXlCl5tSYI3BVaSYjrFYkM,2761
|
|
18
18
|
lamindb_setup/_setup_user.py,sha256=8BSGsW5jfmB4FlkhMt5osYXBbVCdOQeAVATb-oAYxa0,4495
|
|
19
19
|
lamindb_setup/_silence_loggers.py,sha256=AKF_YcHvX32eGXdsYK8MJlxEaZ-Uo2f6QDRzjKFCtws,1568
|
|
20
20
|
lamindb_setup/errors.py,sha256=H1UM-bii0U2vPyjprOBgZK4ijZJgzgCViyGWPd8v5yU,1493
|
|
@@ -31,10 +31,10 @@ lamindb_setup/core/_hub_crud.py,sha256=Jz0d8wFKM1Pv9B9byyUJPlCIMkIzk56Jd-c3Awpm9
|
|
|
31
31
|
lamindb_setup/core/_hub_utils.py,sha256=6dyDGyzYFgVfR_lE3VN3CP1jGp98gxPtr-T91PAP05U,2687
|
|
32
32
|
lamindb_setup/core/_private_django_api.py,sha256=By63l3vIEtK1pq246FhHq3tslxsaTJGKm5VakYluWp4,2656
|
|
33
33
|
lamindb_setup/core/_settings.py,sha256=0la2eYAHcdMYophheGsgQaD2nJGQrPuX4jMV5GKDiC0,12946
|
|
34
|
-
lamindb_setup/core/_settings_instance.py,sha256=
|
|
34
|
+
lamindb_setup/core/_settings_instance.py,sha256=iG4Me-9e9Z4yj3EG3YB3OtkiZ-j-u8R1WPXIeQhMNmw,20484
|
|
35
35
|
lamindb_setup/core/_settings_load.py,sha256=JWd0_hBy04xjKo-tH4y8C9RkaywjrmoT0PsKzVme0n4,5176
|
|
36
36
|
lamindb_setup/core/_settings_save.py,sha256=XZx-vow7BT6y3JpRBB2UOJp2vwc7jOGea4wSgOPqjPU,3262
|
|
37
|
-
lamindb_setup/core/_settings_storage.py,sha256=
|
|
37
|
+
lamindb_setup/core/_settings_storage.py,sha256=wVwDDD51UsJz5gS6juERbWHCdyucP0R2DLaUGhnLUpQ,17079
|
|
38
38
|
lamindb_setup/core/_settings_store.py,sha256=QmeWIGdIyq7UmjfHiEB_0xRD8hY-8-ZR2WntIKfwTKI,2714
|
|
39
39
|
lamindb_setup/core/_settings_user.py,sha256=K2a6nQ0fhEiSb9mCY_p6ItNrHZ3J_j7EfO7CjZap9aA,1462
|
|
40
40
|
lamindb_setup/core/_setup_bionty_sources.py,sha256=ox3X-SHiHa2lNPSWjwZhINypbLacX6kGwH6hVVrSFZc,1505
|
|
@@ -43,8 +43,8 @@ lamindb_setup/core/django.py,sha256=dOt1OkUnZeYOo-LTjatQWQFh_MnjRf9IwwvRZhCwdZQ,
|
|
|
43
43
|
lamindb_setup/core/exceptions.py,sha256=qjMzqy_uzPA7mCOdnoWnS_fdA6OWbdZGftz-YYplrY0,84
|
|
44
44
|
lamindb_setup/core/hashing.py,sha256=Y8Uc5uSGTfU6L2R_gb5w8DdHhGRog7RnkK-e9FEMjPY,3680
|
|
45
45
|
lamindb_setup/core/types.py,sha256=T7NwspfRHgIIpYsXDcApks8jkOlGeGRW-YbVLB7jNIo,67
|
|
46
|
-
lamindb_setup/core/upath.py,sha256=
|
|
47
|
-
lamindb_setup-1.
|
|
48
|
-
lamindb_setup-1.
|
|
49
|
-
lamindb_setup-1.
|
|
50
|
-
lamindb_setup-1.
|
|
46
|
+
lamindb_setup/core/upath.py,sha256=UDPcZaNp3WtZSYrX8hGQ1lq214dBn079bz0FPWFjIbA,34449
|
|
47
|
+
lamindb_setup-1.8.0.dist-info/LICENSE,sha256=UOZ1F5fFDe3XXvG4oNnkL1-Ecun7zpHzRxjp-XsMeAo,11324
|
|
48
|
+
lamindb_setup-1.8.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
|
49
|
+
lamindb_setup-1.8.0.dist-info/METADATA,sha256=oxlCgEfbGqZ8zUi2aKMyQdCZoVTkngUl1SKM-Dc1mdU,1797
|
|
50
|
+
lamindb_setup-1.8.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|