lamindb_setup 0.71.3__py2.py3-none-any.whl → 0.72.0__py2.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/_connect_instance.py +78 -61
- lamindb_setup/_delete.py +10 -49
- lamindb_setup/_init_instance.py +18 -17
- lamindb_setup/_set_managed_storage.py +4 -0
- lamindb_setup/core/_aws_credentials.py +140 -0
- lamindb_setup/core/_hub_core.py +21 -11
- lamindb_setup/core/_settings_instance.py +16 -17
- lamindb_setup/core/_settings_storage.py +18 -2
- lamindb_setup/core/hashing.py +23 -20
- lamindb_setup/core/upath.py +4 -68
- {lamindb_setup-0.71.3.dist-info → lamindb_setup-0.72.0.dist-info}/METADATA +1 -1
- {lamindb_setup-0.71.3.dist-info → lamindb_setup-0.72.0.dist-info}/RECORD +15 -14
- {lamindb_setup-0.71.3.dist-info → lamindb_setup-0.72.0.dist-info}/LICENSE +0 -0
- {lamindb_setup-0.71.3.dist-info → lamindb_setup-0.72.0.dist-info}/WHEEL +0 -0
lamindb_setup/__init__.py
CHANGED
|
@@ -55,7 +55,10 @@ def check_db_dsn_equal_up_to_credentials(db_dsn_hub, db_dsn_local):
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
def update_db_using_local(
|
|
58
|
-
hub_instance_result: dict[str, str],
|
|
58
|
+
hub_instance_result: dict[str, str],
|
|
59
|
+
settings_file: Path,
|
|
60
|
+
db: str | None = None,
|
|
61
|
+
raise_permission_error=True,
|
|
59
62
|
) -> str | None:
|
|
60
63
|
db_updated = None
|
|
61
64
|
# check if postgres
|
|
@@ -77,7 +80,11 @@ def update_db_using_local(
|
|
|
77
80
|
db_dsn_local = LaminDsnModel(db=isettings.db)
|
|
78
81
|
else:
|
|
79
82
|
# just take the default hub result and ensure there is actually a user
|
|
80
|
-
if
|
|
83
|
+
if (
|
|
84
|
+
db_dsn_hub.db.user == "none"
|
|
85
|
+
and db_dsn_hub.db.password == "none"
|
|
86
|
+
and raise_permission_error
|
|
87
|
+
):
|
|
81
88
|
raise PermissionError(
|
|
82
89
|
"No database access, please ask your admin to provide you with"
|
|
83
90
|
" a DB URL and pass it via --db <db_url>"
|
|
@@ -101,6 +108,65 @@ def update_db_using_local(
|
|
|
101
108
|
return db_updated
|
|
102
109
|
|
|
103
110
|
|
|
111
|
+
def _connect_instance(
|
|
112
|
+
owner: str,
|
|
113
|
+
name: str,
|
|
114
|
+
*,
|
|
115
|
+
db: str | None = None,
|
|
116
|
+
raise_permission_error: bool = True,
|
|
117
|
+
) -> InstanceSettings:
|
|
118
|
+
settings_file = instance_settings_file(name, owner)
|
|
119
|
+
make_hub_request = True
|
|
120
|
+
if settings_file.exists():
|
|
121
|
+
isettings = load_instance_settings(settings_file)
|
|
122
|
+
# skip hub request for a purely local instance
|
|
123
|
+
make_hub_request = isettings.is_remote
|
|
124
|
+
if make_hub_request:
|
|
125
|
+
# the following will return a string if the instance does not exist
|
|
126
|
+
# on the hub
|
|
127
|
+
hub_result = load_instance_from_hub(owner=owner, name=name)
|
|
128
|
+
# if hub_result is not a string, it means it made a request
|
|
129
|
+
# that successfully returned metadata
|
|
130
|
+
if not isinstance(hub_result, str):
|
|
131
|
+
instance_result, storage_result = hub_result
|
|
132
|
+
db_updated = update_db_using_local(
|
|
133
|
+
instance_result,
|
|
134
|
+
settings_file,
|
|
135
|
+
db=db,
|
|
136
|
+
raise_permission_error=raise_permission_error,
|
|
137
|
+
)
|
|
138
|
+
ssettings = StorageSettings(
|
|
139
|
+
root=storage_result["root"],
|
|
140
|
+
region=storage_result["region"],
|
|
141
|
+
uid=storage_result["lnid"],
|
|
142
|
+
uuid=UUID(storage_result["id"]),
|
|
143
|
+
instance_id=UUID(instance_result["id"]),
|
|
144
|
+
)
|
|
145
|
+
isettings = InstanceSettings(
|
|
146
|
+
id=UUID(instance_result["id"]),
|
|
147
|
+
owner=owner,
|
|
148
|
+
name=name,
|
|
149
|
+
storage=ssettings,
|
|
150
|
+
db=db_updated,
|
|
151
|
+
schema=instance_result["schema_str"],
|
|
152
|
+
git_repo=instance_result["git_repo"],
|
|
153
|
+
keep_artifacts_local=bool(instance_result["keep_artifacts_local"]),
|
|
154
|
+
is_on_hub=True,
|
|
155
|
+
)
|
|
156
|
+
check_whether_migrations_in_sync(instance_result["lamindb_version"])
|
|
157
|
+
else:
|
|
158
|
+
message = INSTANCE_NOT_FOUND_MESSAGE.format(
|
|
159
|
+
owner=owner, name=name, hub_result=hub_result
|
|
160
|
+
)
|
|
161
|
+
if settings_file.exists():
|
|
162
|
+
isettings = load_instance_settings(settings_file)
|
|
163
|
+
if isettings.is_remote:
|
|
164
|
+
raise InstanceNotFoundError(message)
|
|
165
|
+
else:
|
|
166
|
+
raise InstanceNotFoundError(message)
|
|
167
|
+
return isettings
|
|
168
|
+
|
|
169
|
+
|
|
104
170
|
@unlock_cloud_sqlite_upon_exception(ignore_prev_locker=True)
|
|
105
171
|
def connect(
|
|
106
172
|
slug: str,
|
|
@@ -134,71 +200,22 @@ def connect(
|
|
|
134
200
|
elif settings._instance_exists and f"{owner}/{name}" != settings.instance.slug:
|
|
135
201
|
close_instance(mute=True)
|
|
136
202
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
# mimic instance_result from hub
|
|
143
|
-
instance_result = {"id": isettings._id.hex}
|
|
144
|
-
# skip hub request for a purely local instance
|
|
145
|
-
make_hub_request = isettings.is_remote
|
|
146
|
-
|
|
147
|
-
if make_hub_request:
|
|
148
|
-
# the following will return a string if the instance does not exist
|
|
149
|
-
# on the hub
|
|
150
|
-
hub_result = load_instance_from_hub(owner=owner, name=name)
|
|
151
|
-
# if hub_result is not a string, it means it made a request
|
|
152
|
-
# that successfully returned metadata
|
|
153
|
-
if not isinstance(hub_result, str):
|
|
154
|
-
instance_result, storage_result = hub_result
|
|
155
|
-
db_updated = update_db_using_local(
|
|
156
|
-
instance_result, settings_file, db=db
|
|
157
|
-
)
|
|
158
|
-
ssettings = StorageSettings(
|
|
159
|
-
root=storage_result["root"],
|
|
160
|
-
region=storage_result["region"],
|
|
161
|
-
uid=storage_result["lnid"],
|
|
162
|
-
uuid=UUID(storage_result["id"]),
|
|
163
|
-
instance_id=UUID(instance_result["id"]),
|
|
164
|
-
)
|
|
165
|
-
isettings = InstanceSettings(
|
|
166
|
-
id=UUID(instance_result["id"]),
|
|
167
|
-
owner=owner,
|
|
168
|
-
name=name,
|
|
169
|
-
storage=ssettings,
|
|
170
|
-
db=db_updated,
|
|
171
|
-
schema=instance_result["schema_str"],
|
|
172
|
-
git_repo=instance_result["git_repo"],
|
|
173
|
-
keep_artifacts_local=bool(instance_result["keep_artifacts_local"]),
|
|
174
|
-
is_on_hub=True,
|
|
175
|
-
)
|
|
176
|
-
check_whether_migrations_in_sync(instance_result["lamindb_version"])
|
|
203
|
+
try:
|
|
204
|
+
isettings = _connect_instance(owner, name, db=db)
|
|
205
|
+
except InstanceNotFoundError as e:
|
|
206
|
+
if _raise_not_found_error:
|
|
207
|
+
raise e
|
|
177
208
|
else:
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if settings_file.exists():
|
|
182
|
-
isettings = load_instance_settings(settings_file)
|
|
183
|
-
if isettings.is_remote:
|
|
184
|
-
if _raise_not_found_error:
|
|
185
|
-
raise InstanceNotFoundError(message)
|
|
186
|
-
return "instance-not-found"
|
|
187
|
-
|
|
188
|
-
else:
|
|
189
|
-
if _raise_not_found_error:
|
|
190
|
-
raise InstanceNotFoundError(message)
|
|
191
|
-
return "instance-not-found"
|
|
192
|
-
|
|
209
|
+
return "instance-not-found"
|
|
210
|
+
if isinstance(isettings, str):
|
|
211
|
+
return isettings
|
|
193
212
|
if storage is not None:
|
|
194
213
|
update_isettings_with_storage(isettings, storage)
|
|
195
214
|
isettings._persist()
|
|
196
215
|
if _test:
|
|
197
216
|
return None
|
|
198
217
|
silence_loggers()
|
|
199
|
-
check, msg = isettings._load_db(
|
|
200
|
-
do_not_lock_for_laminapp_admin=True
|
|
201
|
-
) # this also updates local SQLite
|
|
218
|
+
check, msg = isettings._load_db()
|
|
202
219
|
if not check:
|
|
203
220
|
local_db = (
|
|
204
221
|
isettings._is_cloud_sqlite and isettings._sqlite_file_local.exists()
|
|
@@ -216,7 +233,7 @@ def connect(
|
|
|
216
233
|
f"instance exists with id {isettings._id.hex}, but database is not"
|
|
217
234
|
" loadable: re-initializing"
|
|
218
235
|
)
|
|
219
|
-
return "instance-corrupted-or-deleted"
|
|
236
|
+
return "instance-corrupted-or-deleted"
|
|
220
237
|
# this is for testing purposes only
|
|
221
238
|
if _TEST_FAILED_LOAD:
|
|
222
239
|
raise RuntimeError("Technical testing error.")
|
lamindb_setup/_delete.py
CHANGED
|
@@ -1,29 +1,24 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import shutil
|
|
4
|
-
from typing import TYPE_CHECKING
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
5
|
from uuid import UUID
|
|
6
6
|
|
|
7
7
|
from lamin_utils import logger
|
|
8
8
|
|
|
9
|
-
from ._connect_instance import
|
|
10
|
-
|
|
11
|
-
InstanceNotFoundError,
|
|
12
|
-
get_owner_name_from_identifier,
|
|
13
|
-
)
|
|
14
|
-
from .core._hub_core import connect_instance as load_instance_from_hub
|
|
9
|
+
from ._connect_instance import _connect_instance, get_owner_name_from_identifier
|
|
10
|
+
from .core._aws_credentials import HOSTED_BUCKETS
|
|
15
11
|
from .core._hub_core import delete_instance as delete_instance_on_hub
|
|
16
12
|
from .core._hub_core import get_storage_records_for_instance
|
|
17
13
|
from .core._settings import settings
|
|
18
|
-
from .core._settings_instance import InstanceSettings
|
|
19
|
-
from .core._settings_load import load_instance_settings
|
|
20
14
|
from .core._settings_storage import StorageSettings
|
|
21
|
-
from .core.
|
|
22
|
-
from .core.upath import HOSTED_BUCKETS, check_storage_is_empty
|
|
15
|
+
from .core.upath import check_storage_is_empty
|
|
23
16
|
|
|
24
17
|
if TYPE_CHECKING:
|
|
25
18
|
from pathlib import Path
|
|
26
19
|
|
|
20
|
+
from .core._settings_instance import InstanceSettings
|
|
21
|
+
|
|
27
22
|
|
|
28
23
|
def delete_cache(cache_dir: Path):
|
|
29
24
|
if cache_dir is not None and cache_dir.exists():
|
|
@@ -67,42 +62,8 @@ def delete(slug: str, force: bool = False, require_empty: bool = True) -> int |
|
|
|
67
62
|
force: Whether to skip the confirmation prompt.
|
|
68
63
|
require_empty: Whether to check if the instance is empty before deleting.
|
|
69
64
|
"""
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
isettings = settings.instance
|
|
73
|
-
else:
|
|
74
|
-
settings_file = instance_settings_file(instance_name, instance_owner)
|
|
75
|
-
if not settings_file.exists():
|
|
76
|
-
hub_result = load_instance_from_hub(
|
|
77
|
-
owner=instance_owner, name=instance_name
|
|
78
|
-
)
|
|
79
|
-
if isinstance(hub_result, str):
|
|
80
|
-
message = INSTANCE_NOT_FOUND_MESSAGE.format(
|
|
81
|
-
owner=instance_owner,
|
|
82
|
-
name=instance_name,
|
|
83
|
-
hub_result=hub_result,
|
|
84
|
-
)
|
|
85
|
-
raise InstanceNotFoundError(message)
|
|
86
|
-
instance_result, storage_result = hub_result
|
|
87
|
-
ssettings = StorageSettings(
|
|
88
|
-
root=storage_result["root"],
|
|
89
|
-
region=storage_result["region"],
|
|
90
|
-
uid=storage_result["lnid"],
|
|
91
|
-
uuid=UUID(storage_result["id"]),
|
|
92
|
-
)
|
|
93
|
-
isettings = InstanceSettings(
|
|
94
|
-
id=UUID(instance_result["id"]),
|
|
95
|
-
owner=instance_owner,
|
|
96
|
-
name=instance_name,
|
|
97
|
-
storage=ssettings,
|
|
98
|
-
keep_artifacts_local=bool(instance_result["keep_artifacts_local"]),
|
|
99
|
-
db=instance_result["db"] if "db" in instance_result else None,
|
|
100
|
-
schema=instance_result["schema_str"],
|
|
101
|
-
git_repo=instance_result["git_repo"],
|
|
102
|
-
is_on_hub=True,
|
|
103
|
-
)
|
|
104
|
-
else:
|
|
105
|
-
isettings = load_instance_settings(settings_file)
|
|
65
|
+
owner, name = get_owner_name_from_identifier(slug)
|
|
66
|
+
isettings = _connect_instance(owner, name, raise_permission_error=False)
|
|
106
67
|
if isettings.dialect != "sqlite":
|
|
107
68
|
logger.warning(
|
|
108
69
|
f"delete() does not yet affect your Postgres database at {isettings.db}"
|
|
@@ -149,11 +110,11 @@ def delete(slug: str, force: bool = False, require_empty: bool = True) -> int |
|
|
|
149
110
|
for storage_record in storage_records:
|
|
150
111
|
if storage_record["root"] == isettings.storage.root_as_str:
|
|
151
112
|
continue
|
|
113
|
+
ssettings = StorageSettings(storage_record["root"]) # type: ignore
|
|
152
114
|
check_storage_is_empty(
|
|
153
|
-
|
|
115
|
+
ssettings.root, # type: ignore
|
|
154
116
|
raise_error=require_empty,
|
|
155
117
|
)
|
|
156
|
-
ssettings = StorageSettings(storage_record["root"]) # type: ignore
|
|
157
118
|
if ssettings._mark_storage_root.exists():
|
|
158
119
|
ssettings._mark_storage_root.unlink(
|
|
159
120
|
missing_ok=True # this is totally weird, but needed on Py3.11
|
lamindb_setup/_init_instance.py
CHANGED
|
@@ -44,15 +44,17 @@ def register_storage_in_instance(ssettings: StorageSettings):
|
|
|
44
44
|
|
|
45
45
|
from .core.hashing import hash_and_encode_as_b62
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
if ssettings._instance_id is not None:
|
|
48
|
+
instance_uid = hash_and_encode_as_b62(ssettings._instance_id.hex)[:12]
|
|
49
|
+
else:
|
|
50
|
+
instance_uid = None
|
|
49
51
|
# how do we ensure that this function is only called passing
|
|
50
52
|
# the managing instance?
|
|
51
53
|
defaults = {
|
|
52
54
|
"root": ssettings.root_as_str,
|
|
53
55
|
"type": ssettings.type,
|
|
54
56
|
"region": ssettings.region,
|
|
55
|
-
"instance_uid":
|
|
57
|
+
"instance_uid": instance_uid,
|
|
56
58
|
"created_by_id": current_user_id(),
|
|
57
59
|
}
|
|
58
60
|
if ssettings._uid is not None:
|
|
@@ -67,20 +69,19 @@ def register_storage_in_instance(ssettings: StorageSettings):
|
|
|
67
69
|
def register_user(usettings):
|
|
68
70
|
from lnschema_core.models import User
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
try
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
pass
|
|
72
|
+
try:
|
|
73
|
+
# need to have try except because of integer primary key migration
|
|
74
|
+
user, created = User.objects.update_or_create(
|
|
75
|
+
uid=usettings.uid,
|
|
76
|
+
defaults={
|
|
77
|
+
"handle": usettings.handle,
|
|
78
|
+
"name": usettings.name,
|
|
79
|
+
},
|
|
80
|
+
)
|
|
81
|
+
# for users with only read access, except via ProgrammingError
|
|
82
|
+
# ProgrammingError: permission denied for table lnschema_core_user
|
|
83
|
+
except (OperationalError, FieldError, ProgrammingError):
|
|
84
|
+
pass
|
|
84
85
|
|
|
85
86
|
|
|
86
87
|
def register_user_and_storage_in_instance(isettings: InstanceSettings, usettings):
|
|
@@ -31,6 +31,10 @@ def set_managed_storage(root: UPathStr, **fs_kwargs):
|
|
|
31
31
|
ssettings = init_storage(
|
|
32
32
|
root=root, instance_id=settings.instance._id, register_hub=True
|
|
33
33
|
)
|
|
34
|
+
if ssettings._instance_id is None:
|
|
35
|
+
raise ValueError(
|
|
36
|
+
f"Cannot manage storage without write access: {ssettings.root}"
|
|
37
|
+
)
|
|
34
38
|
settings.instance._storage = ssettings
|
|
35
39
|
settings.instance._persist() # this also updates the settings object
|
|
36
40
|
register_storage_in_instance(ssettings)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
|
|
6
|
+
from upath.implementations.cloud import S3Path
|
|
7
|
+
|
|
8
|
+
HOSTED_REGIONS = [
|
|
9
|
+
"eu-central-1",
|
|
10
|
+
"eu-west-2",
|
|
11
|
+
"us-east-1",
|
|
12
|
+
"us-east-2",
|
|
13
|
+
"us-west-1",
|
|
14
|
+
"us-west-2",
|
|
15
|
+
]
|
|
16
|
+
lamin_env = os.getenv("LAMIN_ENV")
|
|
17
|
+
if lamin_env is None or lamin_env == "prod":
|
|
18
|
+
hosted_buckets_list = [f"s3://lamin-{region}" for region in HOSTED_REGIONS]
|
|
19
|
+
hosted_buckets_list.append("s3://scverse-spatial-eu-central-1")
|
|
20
|
+
HOSTED_BUCKETS = tuple(hosted_buckets_list)
|
|
21
|
+
else:
|
|
22
|
+
HOSTED_BUCKETS = ("s3://lamin-hosted-test",) # type: ignore
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
AWS_CREDENTIALS_EXPIRATION = 11 * 60 * 60 # refresh credentials after 11 hours
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AWSCredentialsManager:
|
|
29
|
+
def __init__(self):
|
|
30
|
+
self._credentials_cache = {}
|
|
31
|
+
|
|
32
|
+
from s3fs import S3FileSystem
|
|
33
|
+
|
|
34
|
+
# this is cached so will be resued with the connection initialized
|
|
35
|
+
fs = S3FileSystem(cache_regions=True)
|
|
36
|
+
fs.connect()
|
|
37
|
+
self.anon = fs.session._credentials is None
|
|
38
|
+
|
|
39
|
+
def _find_root(self, path_str: str) -> str | None:
|
|
40
|
+
roots = self._credentials_cache.keys()
|
|
41
|
+
if path_str in roots:
|
|
42
|
+
return path_str
|
|
43
|
+
roots = sorted(roots, key=len, reverse=True)
|
|
44
|
+
for root in roots:
|
|
45
|
+
if path_str.startswith(root):
|
|
46
|
+
return root
|
|
47
|
+
return None
|
|
48
|
+
|
|
49
|
+
def _is_active(self, root: str) -> bool:
|
|
50
|
+
return (
|
|
51
|
+
time.time() - self._credentials_cache[root]["time"]
|
|
52
|
+
) < AWS_CREDENTIALS_EXPIRATION
|
|
53
|
+
|
|
54
|
+
def _set_cached_credentials(self, root: str, credentials: dict):
|
|
55
|
+
if root not in self._credentials_cache:
|
|
56
|
+
self._credentials_cache[root] = {}
|
|
57
|
+
self._credentials_cache[root]["credentials"] = credentials
|
|
58
|
+
self._credentials_cache[root]["time"] = time.time()
|
|
59
|
+
|
|
60
|
+
def _get_cached_credentials(self, root: str) -> dict:
|
|
61
|
+
return self._credentials_cache[root]["credentials"]
|
|
62
|
+
|
|
63
|
+
def _path_inject_options(self, path: S3Path, credentials: dict) -> S3Path:
|
|
64
|
+
if credentials == {}:
|
|
65
|
+
# credentials were specified manually for the path
|
|
66
|
+
if "anon" in path._kwargs:
|
|
67
|
+
anon = path._kwargs["anon"]
|
|
68
|
+
elif path.fs.key is not None and path.fs.secret is not None:
|
|
69
|
+
anon = False
|
|
70
|
+
else:
|
|
71
|
+
anon = self.anon
|
|
72
|
+
connection_options = {"anon": anon}
|
|
73
|
+
else:
|
|
74
|
+
connection_options = credentials
|
|
75
|
+
|
|
76
|
+
if "cache_regions" in path._kwargs:
|
|
77
|
+
cache_regions = path._kwargs["cache_regions"]
|
|
78
|
+
else:
|
|
79
|
+
cache_regions = True
|
|
80
|
+
|
|
81
|
+
return S3Path(path, cache_regions=cache_regions, **connection_options)
|
|
82
|
+
|
|
83
|
+
def enrich_path(self, path: S3Path, access_token: str | None = None) -> S3Path:
|
|
84
|
+
path_str = path.as_posix().rstrip("/")
|
|
85
|
+
root = self._find_root(path_str)
|
|
86
|
+
|
|
87
|
+
if root is not None:
|
|
88
|
+
set_cache = False
|
|
89
|
+
credentials = self._get_cached_credentials(root)
|
|
90
|
+
|
|
91
|
+
if access_token is not None:
|
|
92
|
+
set_cache = True
|
|
93
|
+
elif credentials != {}:
|
|
94
|
+
# update credentials
|
|
95
|
+
if not self._is_active(root):
|
|
96
|
+
set_cache = True
|
|
97
|
+
else:
|
|
98
|
+
set_cache = True
|
|
99
|
+
|
|
100
|
+
if set_cache:
|
|
101
|
+
from ._hub_core import access_aws
|
|
102
|
+
|
|
103
|
+
storage_root_info = access_aws(path_str, access_token=access_token)
|
|
104
|
+
accessibility = storage_root_info["accessibility"]
|
|
105
|
+
|
|
106
|
+
is_managed = accessibility.get("is_managed", False)
|
|
107
|
+
if is_managed:
|
|
108
|
+
credentials = storage_root_info["credentials"]
|
|
109
|
+
else:
|
|
110
|
+
credentials = {}
|
|
111
|
+
|
|
112
|
+
if access_token is None:
|
|
113
|
+
if "storage_root" in accessibility:
|
|
114
|
+
root = accessibility["storage_root"]
|
|
115
|
+
# just to be safe
|
|
116
|
+
root = None if root == "" else root
|
|
117
|
+
if root is None:
|
|
118
|
+
# heuristic
|
|
119
|
+
# do not write the first level for the known hosted buckets
|
|
120
|
+
if path_str.startswith(HOSTED_BUCKETS):
|
|
121
|
+
root = "/".join(path.path.rstrip("/").split("/")[:2])
|
|
122
|
+
else:
|
|
123
|
+
# write the bucket for everything else
|
|
124
|
+
root = path._url.netloc
|
|
125
|
+
root = "s3://" + root
|
|
126
|
+
self._set_cached_credentials(root, credentials)
|
|
127
|
+
|
|
128
|
+
return self._path_inject_options(path, credentials)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
_aws_credentials_manager: AWSCredentialsManager | None = None
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def get_aws_credentials_manager() -> AWSCredentialsManager:
|
|
135
|
+
global _aws_credentials_manager
|
|
136
|
+
|
|
137
|
+
if _aws_credentials_manager is None:
|
|
138
|
+
_aws_credentials_manager = AWSCredentialsManager()
|
|
139
|
+
|
|
140
|
+
return _aws_credentials_manager
|
lamindb_setup/core/_hub_core.py
CHANGED
|
@@ -284,9 +284,11 @@ def _init_instance(isettings: InstanceSettings, client: Client) -> None:
|
|
|
284
284
|
# as then init_instance is no longer idempotent
|
|
285
285
|
try:
|
|
286
286
|
client.table("instance").insert(fields, returning="minimal").execute()
|
|
287
|
-
except APIError
|
|
288
|
-
logger.warning(
|
|
289
|
-
|
|
287
|
+
except APIError:
|
|
288
|
+
logger.warning(
|
|
289
|
+
f"instance already existed at: https://lamin.ai/{isettings.owner}/{isettings.name}"
|
|
290
|
+
)
|
|
291
|
+
return None
|
|
290
292
|
client.table("storage").update(
|
|
291
293
|
{"instance_id": isettings._id.hex, "is_default": True}
|
|
292
294
|
).eq("id", isettings.storage._uuid.hex).execute() # type: ignore
|
|
@@ -351,35 +353,43 @@ def _connect_instance(
|
|
|
351
353
|
return instance, storage
|
|
352
354
|
|
|
353
355
|
|
|
354
|
-
def access_aws(storage_root: str, access_token: str | None = None) -> dict[str,
|
|
356
|
+
def access_aws(storage_root: str, access_token: str | None = None) -> dict[str, dict]:
|
|
355
357
|
from ._settings import settings
|
|
356
358
|
|
|
357
359
|
if settings.user.handle != "anonymous" or access_token is not None:
|
|
358
|
-
|
|
360
|
+
storage_root_info = call_with_fallback_auth(
|
|
359
361
|
_access_aws, storage_root=storage_root, access_token=access_token
|
|
360
362
|
)
|
|
361
|
-
return
|
|
363
|
+
return storage_root_info
|
|
362
364
|
else:
|
|
363
365
|
raise RuntimeError("Can only get access to AWS if authenticated.")
|
|
364
366
|
|
|
365
367
|
|
|
366
|
-
def _access_aws(*, storage_root: str, client: Client) -> dict[str,
|
|
368
|
+
def _access_aws(*, storage_root: str, client: Client) -> dict[str, dict]:
|
|
367
369
|
import lamindb_setup
|
|
368
370
|
|
|
371
|
+
storage_root_info: dict[str, dict] = {"credentials": {}, "accessibility": {}}
|
|
369
372
|
response = client.functions.invoke(
|
|
370
373
|
"access-aws",
|
|
371
374
|
invoke_options={"body": {"storage_root": storage_root}},
|
|
372
375
|
)
|
|
373
376
|
if response is not None and response != b"{}":
|
|
374
|
-
|
|
375
|
-
|
|
377
|
+
data = json.loads(response)
|
|
378
|
+
|
|
379
|
+
loaded_credentials = data["Credentials"]
|
|
380
|
+
loaded_accessibility = data["StorageAccessibility"]
|
|
381
|
+
|
|
382
|
+
credentials = storage_root_info["credentials"]
|
|
376
383
|
credentials["key"] = loaded_credentials["AccessKeyId"]
|
|
377
384
|
credentials["secret"] = loaded_credentials["SecretAccessKey"]
|
|
378
385
|
credentials["token"] = loaded_credentials["SessionToken"]
|
|
379
|
-
|
|
386
|
+
|
|
387
|
+
accessibility = storage_root_info["accessibility"]
|
|
388
|
+
accessibility["storage_root"] = loaded_accessibility["storageRoot"]
|
|
389
|
+
accessibility["is_managed"] = loaded_accessibility["isManaged"]
|
|
380
390
|
elif lamindb_setup._TESTING:
|
|
381
391
|
raise RuntimeError(f"access-aws errored: {response}")
|
|
382
|
-
return
|
|
392
|
+
return storage_root_info
|
|
383
393
|
|
|
384
394
|
|
|
385
395
|
def get_lamin_site_base_url():
|
|
@@ -42,7 +42,7 @@ class InstanceSettings:
|
|
|
42
42
|
db: str | None = None, # DB URI
|
|
43
43
|
schema: str | None = None, # comma-separated string of schema names
|
|
44
44
|
git_repo: str | None = None, # a git repo URL
|
|
45
|
-
is_on_hub: bool =
|
|
45
|
+
is_on_hub: bool | None = None, # initialized from hub
|
|
46
46
|
):
|
|
47
47
|
from ._hub_utils import validate_db_arg
|
|
48
48
|
|
|
@@ -367,17 +367,25 @@ class InstanceSettings:
|
|
|
367
367
|
|
|
368
368
|
@property
|
|
369
369
|
def is_on_hub(self) -> bool:
|
|
370
|
-
"""Is this instance on the hub
|
|
370
|
+
"""Is this instance on the hub?
|
|
371
371
|
|
|
372
|
-
|
|
372
|
+
Can only reliably establish if user has access to the instance. Will
|
|
373
|
+
return `False` in case the instance isn't found.
|
|
373
374
|
"""
|
|
374
375
|
if self._is_on_hub is None:
|
|
375
376
|
from ._hub_client import call_with_fallback_auth
|
|
376
377
|
from ._hub_crud import select_instance_by_id
|
|
378
|
+
from ._settings import settings
|
|
377
379
|
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
380
|
+
if settings.user.handle != "anonymous":
|
|
381
|
+
response = call_with_fallback_auth(
|
|
382
|
+
select_instance_by_id, instance_id=self._id.hex
|
|
383
|
+
)
|
|
384
|
+
else:
|
|
385
|
+
response = call_with_fallback(
|
|
386
|
+
select_instance_by_id, instance_id=self._id.hex
|
|
387
|
+
)
|
|
388
|
+
logger.warning("calling anonymously, will miss private instances")
|
|
381
389
|
if response is None:
|
|
382
390
|
self._is_on_hub = False
|
|
383
391
|
else:
|
|
@@ -406,9 +414,7 @@ class InstanceSettings:
|
|
|
406
414
|
|
|
407
415
|
setup_django(self, init=True)
|
|
408
416
|
|
|
409
|
-
def _load_db(
|
|
410
|
-
self, do_not_lock_for_laminapp_admin: bool = False
|
|
411
|
-
) -> tuple[bool, str]:
|
|
417
|
+
def _load_db(self) -> tuple[bool, str]:
|
|
412
418
|
# Is the database available and initialized as LaminDB?
|
|
413
419
|
# returns a tuple of status code and message
|
|
414
420
|
if self.dialect == "sqlite" and not self._sqlite_file.exists():
|
|
@@ -423,15 +429,8 @@ class InstanceSettings:
|
|
|
423
429
|
|
|
424
430
|
from .django import setup_django
|
|
425
431
|
|
|
426
|
-
# lock in all cases except if do_not_lock_for_laminapp_admin is True and
|
|
427
|
-
# user is `laminapp-admin`
|
|
428
|
-
# value doesn't matter if not a cloud sqlite instance
|
|
429
|
-
lock_cloud_sqlite = self._is_cloud_sqlite and (
|
|
430
|
-
not do_not_lock_for_laminapp_admin
|
|
431
|
-
or settings.user.handle != "laminapp-admin"
|
|
432
|
-
)
|
|
433
432
|
# we need the local sqlite to setup django
|
|
434
|
-
self._update_local_sqlite_file(lock_cloud_sqlite=
|
|
433
|
+
self._update_local_sqlite_file(lock_cloud_sqlite=self._is_cloud_sqlite)
|
|
435
434
|
# setting up django also performs a check for migrations & prints them
|
|
436
435
|
# as warnings
|
|
437
436
|
# this should fail, e.g., if the db is not reachable
|
|
@@ -10,11 +10,11 @@ from typing import TYPE_CHECKING, Any, Literal, Optional, Union
|
|
|
10
10
|
from appdirs import AppDirs
|
|
11
11
|
from lamin_utils import logger
|
|
12
12
|
|
|
13
|
+
from ._aws_credentials import HOSTED_REGIONS, get_aws_credentials_manager
|
|
13
14
|
from ._aws_storage import find_closest_aws_region
|
|
14
15
|
from ._settings_save import save_system_storage_settings
|
|
15
16
|
from ._settings_store import system_storage_settings_file
|
|
16
17
|
from .upath import (
|
|
17
|
-
HOSTED_REGIONS,
|
|
18
18
|
LocalPathClasses,
|
|
19
19
|
UPath,
|
|
20
20
|
convert_pathlike,
|
|
@@ -113,11 +113,20 @@ def init_storage(
|
|
|
113
113
|
)
|
|
114
114
|
# the below might update the uid with one that's already taken on the hub
|
|
115
115
|
if ssettings.type_is_cloud or register_hub:
|
|
116
|
+
from ._hub_core import delete_storage_record
|
|
116
117
|
from ._hub_core import init_storage as init_storage_hub
|
|
117
118
|
|
|
118
119
|
init_storage_hub(ssettings)
|
|
119
120
|
# below comes last only if everything else was successful
|
|
120
|
-
|
|
121
|
+
try:
|
|
122
|
+
mark_storage_root(ssettings.root, ssettings.uid) # type: ignore
|
|
123
|
+
except Exception:
|
|
124
|
+
logger.important(
|
|
125
|
+
f"due to lack of write access, LaminDB won't manage storage location: {ssettings.root}"
|
|
126
|
+
)
|
|
127
|
+
if ssettings._uuid is not None:
|
|
128
|
+
delete_storage_record(ssettings._uuid) # type: ignore
|
|
129
|
+
ssettings._instance_id = None
|
|
121
130
|
return ssettings
|
|
122
131
|
|
|
123
132
|
|
|
@@ -142,6 +151,7 @@ class StorageSettings:
|
|
|
142
151
|
uid: str | None = None,
|
|
143
152
|
uuid: UUID | None = None,
|
|
144
153
|
instance_id: UUID | None = None,
|
|
154
|
+
# note that passing access_token prevents credentials caching
|
|
145
155
|
access_token: str | None = None,
|
|
146
156
|
):
|
|
147
157
|
self._uid = uid
|
|
@@ -226,6 +236,12 @@ class StorageSettings:
|
|
|
226
236
|
if self._root is None:
|
|
227
237
|
# below makes network requests to get credentials
|
|
228
238
|
self._root = create_path(self._root_init, access_token=self.access_token)
|
|
239
|
+
elif getattr(self._root, "protocol", "") == "s3":
|
|
240
|
+
# this is needed to be sure that the root always has nonexpired credentials
|
|
241
|
+
# this just checks for time of the cached credentials in most cases
|
|
242
|
+
return get_aws_credentials_manager().enrich_path(
|
|
243
|
+
self._root, access_token=self.access_token
|
|
244
|
+
)
|
|
229
245
|
return self._root
|
|
230
246
|
|
|
231
247
|
def _set_fs_kwargs(self, **kwargs):
|
lamindb_setup/core/hashing.py
CHANGED
|
@@ -40,11 +40,11 @@ def hash_set(s: set[str]) -> str:
|
|
|
40
40
|
return to_b64_str(hashlib.md5(bstr).digest())[:20]
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
def hash_md5s_from_dir(
|
|
43
|
+
def hash_md5s_from_dir(hashes: list[str]) -> tuple[str, str]:
|
|
44
44
|
# need to sort below because we don't want the order of parsing the dir to
|
|
45
45
|
# affect the hash
|
|
46
46
|
digests = b"".join(
|
|
47
|
-
hashlib.md5(
|
|
47
|
+
hashlib.md5(hash.encode("utf-8")).digest() for hash in sorted(hashes)
|
|
48
48
|
)
|
|
49
49
|
digest = hashlib.md5(digests).digest()
|
|
50
50
|
return to_b64_str(digest)[:22], "md5-d"
|
|
@@ -59,24 +59,27 @@ def hash_code(file_path: UPathStr):
|
|
|
59
59
|
return hashlib.sha1(blob)
|
|
60
60
|
|
|
61
61
|
|
|
62
|
-
def hash_file(
|
|
63
|
-
|
|
62
|
+
def hash_file(
|
|
63
|
+
file_path: Path,
|
|
64
|
+
file_size: int | None = None,
|
|
65
|
+
chunk_size: int | None = 50 * 1024 * 1024,
|
|
66
|
+
) -> tuple[str, str]:
|
|
64
67
|
with open(file_path, "rb") as fp:
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if
|
|
70
|
-
|
|
68
|
+
if file_size is None:
|
|
69
|
+
fp.seek(0, 2)
|
|
70
|
+
file_size = fp.tell()
|
|
71
|
+
fp.seek(0, 0)
|
|
72
|
+
if chunk_size is None:
|
|
73
|
+
chunk_size = file_size
|
|
74
|
+
first_chunk = fp.read(chunk_size)
|
|
75
|
+
if file_size <= chunk_size:
|
|
76
|
+
digest = hashlib.md5(first_chunk).digest()
|
|
77
|
+
hash_type = "md5"
|
|
78
|
+
else:
|
|
71
79
|
fp.seek(-chunk_size, 2)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
hash_type = "md5"
|
|
78
|
-
else:
|
|
79
|
-
digests = b"".join(hashlib.sha1(chunk).digest() for chunk in chunks)
|
|
80
|
-
digest = hashlib.sha1(digests).digest()
|
|
81
|
-
hash_type = "sha1-fl" # sha1 first last chunk
|
|
80
|
+
last_chunk = fp.read(chunk_size)
|
|
81
|
+
digest = hashlib.sha1(
|
|
82
|
+
hashlib.sha1(first_chunk).digest() + hashlib.sha1(last_chunk).digest()
|
|
83
|
+
).digest()
|
|
84
|
+
hash_type = "sha1-fl"
|
|
82
85
|
return to_b64_str(digest)[:22], hash_type
|
lamindb_setup/core/upath.py
CHANGED
|
@@ -11,13 +11,13 @@ from itertools import islice
|
|
|
11
11
|
from pathlib import Path, PurePosixPath
|
|
12
12
|
from typing import TYPE_CHECKING, Any, Literal
|
|
13
13
|
|
|
14
|
-
import botocore.session
|
|
15
14
|
import fsspec
|
|
16
15
|
from lamin_utils import logger
|
|
17
16
|
from upath import UPath
|
|
18
17
|
from upath.implementations.cloud import CloudPath, S3Path # keep CloudPath!
|
|
19
18
|
from upath.implementations.local import LocalPath, PosixUPath, WindowsUPath
|
|
20
19
|
|
|
20
|
+
from ._aws_credentials import HOSTED_BUCKETS, get_aws_credentials_manager
|
|
21
21
|
from .hashing import b16_to_b64, hash_md5s_from_dir
|
|
22
22
|
|
|
23
23
|
if TYPE_CHECKING:
|
|
@@ -158,7 +158,8 @@ def print_hook(size: int, value: int, objectname: str, action: str):
|
|
|
158
158
|
progress_in_percent = (value / size) * 100
|
|
159
159
|
out = f"... {action} {objectname}:" f" {min(progress_in_percent, 100):4.1f}%"
|
|
160
160
|
if "NBPRJ_TEST_NBPATH" not in os.environ:
|
|
161
|
-
|
|
161
|
+
end = "\n" if progress_in_percent >= 100 else "\r"
|
|
162
|
+
print(out, end=end)
|
|
162
163
|
|
|
163
164
|
|
|
164
165
|
class ProgressCallback(fsspec.callbacks.Callback):
|
|
@@ -670,77 +671,12 @@ def convert_pathlike(pathlike: UPathStr) -> UPath:
|
|
|
670
671
|
return path
|
|
671
672
|
|
|
672
673
|
|
|
673
|
-
HOSTED_REGIONS = [
|
|
674
|
-
"eu-central-1",
|
|
675
|
-
"eu-west-2",
|
|
676
|
-
"us-east-1",
|
|
677
|
-
"us-east-2",
|
|
678
|
-
"us-west-1",
|
|
679
|
-
"us-west-2",
|
|
680
|
-
]
|
|
681
|
-
lamin_env = os.getenv("LAMIN_ENV")
|
|
682
|
-
if lamin_env is None or lamin_env == "prod":
|
|
683
|
-
hosted_buckets_list = [f"s3://lamin-{region}" for region in HOSTED_REGIONS]
|
|
684
|
-
hosted_buckets_list.append("s3://scverse-spatial-eu-central-1")
|
|
685
|
-
HOSTED_BUCKETS = tuple(hosted_buckets_list)
|
|
686
|
-
else:
|
|
687
|
-
HOSTED_BUCKETS = ("s3://lamin-hosted-test",) # type: ignore
|
|
688
|
-
credentials_cache: dict[str, dict[str, str]] = {}
|
|
689
|
-
AWS_CREDENTIALS_PRESENT = None
|
|
690
|
-
|
|
691
|
-
|
|
692
674
|
def create_path(path: UPath, access_token: str | None = None) -> UPath:
|
|
693
675
|
path = convert_pathlike(path)
|
|
694
676
|
# test whether we have an AWS S3 path
|
|
695
677
|
if not isinstance(path, S3Path):
|
|
696
678
|
return path
|
|
697
|
-
|
|
698
|
-
global AWS_CREDENTIALS_PRESENT
|
|
699
|
-
|
|
700
|
-
if AWS_CREDENTIALS_PRESENT is not None:
|
|
701
|
-
anon = AWS_CREDENTIALS_PRESENT
|
|
702
|
-
else:
|
|
703
|
-
if path.fs.key is not None and path.fs.secret is not None:
|
|
704
|
-
anon = False
|
|
705
|
-
else:
|
|
706
|
-
# we could do path.fs.connect()
|
|
707
|
-
# and check path.fs.session._credentials, but it'd be slower
|
|
708
|
-
session = botocore.session.get_session()
|
|
709
|
-
credentials = session.get_credentials()
|
|
710
|
-
if credentials is None or credentials.access_key is None:
|
|
711
|
-
anon = True
|
|
712
|
-
else:
|
|
713
|
-
anon = False
|
|
714
|
-
# cache credentials and reuse further
|
|
715
|
-
AWS_CREDENTIALS_PRESENT = anon
|
|
716
|
-
|
|
717
|
-
# test whether we are on hosted storage or not
|
|
718
|
-
path_str = path.as_posix()
|
|
719
|
-
is_hosted_storage = path_str.startswith(HOSTED_BUCKETS)
|
|
720
|
-
|
|
721
|
-
if not is_hosted_storage:
|
|
722
|
-
# make anon request if no credentials present
|
|
723
|
-
return UPath(path, cache_regions=True, anon=anon)
|
|
724
|
-
|
|
725
|
-
root_folder = "/".join(path_str.replace("s3://", "").split("/")[:2])
|
|
726
|
-
|
|
727
|
-
if access_token is None and root_folder in credentials_cache:
|
|
728
|
-
credentials = credentials_cache[root_folder]
|
|
729
|
-
else:
|
|
730
|
-
from ._hub_core import access_aws
|
|
731
|
-
|
|
732
|
-
credentials = access_aws(
|
|
733
|
-
storage_root=f"s3://{root_folder}", access_token=access_token
|
|
734
|
-
)
|
|
735
|
-
if access_token is None:
|
|
736
|
-
credentials_cache[root_folder] = credentials
|
|
737
|
-
|
|
738
|
-
return UPath(
|
|
739
|
-
path,
|
|
740
|
-
key=credentials["key"],
|
|
741
|
-
secret=credentials["secret"],
|
|
742
|
-
token=credentials["token"],
|
|
743
|
-
)
|
|
679
|
+
return get_aws_credentials_manager().enrich_path(path, access_token)
|
|
744
680
|
|
|
745
681
|
|
|
746
682
|
def get_stat_file_cloud(stat: dict) -> tuple[int, str, str]:
|
|
@@ -1,43 +1,44 @@
|
|
|
1
|
-
lamindb_setup/__init__.py,sha256=
|
|
1
|
+
lamindb_setup/__init__.py,sha256=4RLRB4zcYMk9ztnkPkCMOiGJ1hmqyhLXUsSRwtN932I,1542
|
|
2
2
|
lamindb_setup/_cache.py,sha256=wA7mbysANwe8hPNbjDo9bOmXJ0xIyaS5iyxIpxSWji4,846
|
|
3
3
|
lamindb_setup/_check.py,sha256=28PcG8Kp6OpjSLSi1r2boL2Ryeh6xkaCL87HFbjs6GA,129
|
|
4
4
|
lamindb_setup/_check_setup.py,sha256=cNEL9Q4yPpmEkGKHH8JgullWl1VUZwALJ4RHn9wZypY,2613
|
|
5
5
|
lamindb_setup/_close.py,sha256=1QS9p2SCacgovYn6xqWU4zFvwHN1RgIccvzwJgFvKgU,1186
|
|
6
|
-
lamindb_setup/_connect_instance.py,sha256=
|
|
7
|
-
lamindb_setup/_delete.py,sha256=
|
|
6
|
+
lamindb_setup/_connect_instance.py,sha256=C4WURprypLpvS1oJmfyXHvXCMPc7yOOvKmOabyFr7dE,12728
|
|
7
|
+
lamindb_setup/_delete.py,sha256=Y8KSFYgY0UHAvjd7cCL6hZ_XiLeJwx50BguVATcj_Xo,5524
|
|
8
8
|
lamindb_setup/_django.py,sha256=EoyWvFzH0i9wxjy4JZhcoXCTckztP_Mrl6FbYQnMmLE,1534
|
|
9
9
|
lamindb_setup/_exportdb.py,sha256=uTIZjKKTB7arzEr1j0O6lONiT2pRBKeOFdLvOV8ZwzE,2120
|
|
10
10
|
lamindb_setup/_importdb.py,sha256=yYYShzUajTsR-cTW4CZ-UNDWZY2uE5PAgNbp-wn8Ogc,1874
|
|
11
|
-
lamindb_setup/_init_instance.py,sha256=
|
|
11
|
+
lamindb_setup/_init_instance.py,sha256=lR-6txbf3Z2O7ki-DMZWFg36QNiUZ_B5lc6JXjScdus,11897
|
|
12
12
|
lamindb_setup/_migrate.py,sha256=4nBTFg5-BK4A2gH-D3_tcFf8EtvMnIo5Mq0e_C6_9-U,8815
|
|
13
13
|
lamindb_setup/_register_instance.py,sha256=Jeu0wyvJVSVQ_n-A_7yn7xOZIP0ncJD92DRABqzPIjA,940
|
|
14
14
|
lamindb_setup/_schema.py,sha256=b3uzhhWpV5mQtDwhMINc2MabGCnGLESy51ito3yl6Wc,679
|
|
15
|
-
lamindb_setup/_set_managed_storage.py,sha256=
|
|
15
|
+
lamindb_setup/_set_managed_storage.py,sha256=mNZrANn-9rwZ0oGWxxg0wS0T0VOQCWyo4nSSyNAE15Q,1419
|
|
16
16
|
lamindb_setup/_setup_user.py,sha256=6Oc7Rke-yRQSZbuntdUAz8QbJ6UuPzYHI9FnYlf_q-A,3670
|
|
17
17
|
lamindb_setup/_silence_loggers.py,sha256=AKF_YcHvX32eGXdsYK8MJlxEaZ-Uo2f6QDRzjKFCtws,1568
|
|
18
18
|
lamindb_setup/core/__init__.py,sha256=dV9S-rQpNK9JcBn4hiEmiLnmNqfpPFJD9pqagMCaIew,416
|
|
19
|
+
lamindb_setup/core/_aws_credentials.py,sha256=0vDs7pWFguBE5UHXf4ysvsjieWygye-Akig6E2Ewl-Q,4749
|
|
19
20
|
lamindb_setup/core/_aws_storage.py,sha256=nEjeUv4xUVpoV0Lx-zjjmyb9w804bDyaeiM-OqbfwM0,1799
|
|
20
21
|
lamindb_setup/core/_deprecated.py,sha256=3qxUI1dnDlSeR0BYrv7ucjqRBEojbqotPgpShXs4KF8,2520
|
|
21
22
|
lamindb_setup/core/_docs.py,sha256=3k-YY-oVaJd_9UIY-LfBg_u8raKOCNfkZQPA73KsUhs,276
|
|
22
23
|
lamindb_setup/core/_hub_client.py,sha256=V0qKDsCdRn-tQy2YIk4VgXcpJFmuum6N3gcavAC7gBQ,5504
|
|
23
|
-
lamindb_setup/core/_hub_core.py,sha256=
|
|
24
|
+
lamindb_setup/core/_hub_core.py,sha256=GSPPwBQlqg1l3TfzoaDulcG8u_HAjeqqBgGUJQVY4HQ,16447
|
|
24
25
|
lamindb_setup/core/_hub_crud.py,sha256=b1XF7AJpM9Q-ttm9nPG-r3OTRWHQaGzAGIyvmb83NTo,4859
|
|
25
26
|
lamindb_setup/core/_hub_utils.py,sha256=b_M1LkdCjiMWm1EOlSb9GuPdLijwVgQDtATTpeZuXI0,1875
|
|
26
27
|
lamindb_setup/core/_settings.py,sha256=jjZ_AxRXB3Y3UP6m04BAw_dhFbJbdg2-nZWmEv2LNZ8,3141
|
|
27
|
-
lamindb_setup/core/_settings_instance.py,sha256=
|
|
28
|
+
lamindb_setup/core/_settings_instance.py,sha256=w5SBrp6nIJMegzNJSnfQl8HvqEtwgmR7OrayffVedLc,16612
|
|
28
29
|
lamindb_setup/core/_settings_load.py,sha256=NGgCDpN85j1EqoKlrYFIlZBMlBJm33gx2-wc96CP_ZQ,3922
|
|
29
30
|
lamindb_setup/core/_settings_save.py,sha256=d1A-Ex-7H08mb8l7I0Oe0j0GilrfaDuprh_NMxhQAsQ,2704
|
|
30
|
-
lamindb_setup/core/_settings_storage.py,sha256=
|
|
31
|
+
lamindb_setup/core/_settings_storage.py,sha256=7f0jt1zcSltpOYDPQ5CVvbBon_d7aneKTte935-2REY,13236
|
|
31
32
|
lamindb_setup/core/_settings_store.py,sha256=dagS5c7wAMRnuZTRfCU4sKaIOyF_HwAP5Fnnn8vphno,2084
|
|
32
33
|
lamindb_setup/core/_settings_user.py,sha256=P2lC4WDRAFfT-Xq3MlXJ-wMKIHCoGNhMTQfRGIAyUNQ,1344
|
|
33
34
|
lamindb_setup/core/_setup_bionty_sources.py,sha256=OgPpZxN2_Wffy-ogEBz_97c_k8d2bD-DDVt89-u9GLY,3002
|
|
34
35
|
lamindb_setup/core/cloud_sqlite_locker.py,sha256=NIBNAGq7TTRrip9OzMdiQKj8QOuwhL9esyM0aehUqBA,6893
|
|
35
36
|
lamindb_setup/core/django.py,sha256=QUQm3zt5QIiD8uv6o9vbSm_bshqiSWzKSkgD3z2eJCg,3542
|
|
36
37
|
lamindb_setup/core/exceptions.py,sha256=eoI7AXgATgDVzgArtN7CUvpaMUC067vsBg5LHCsWzDM,305
|
|
37
|
-
lamindb_setup/core/hashing.py,sha256=
|
|
38
|
+
lamindb_setup/core/hashing.py,sha256=7r96h5JBzuwfOR_gNNqTyWNPKMuiOUfBYwn6sCbZkf8,2269
|
|
38
39
|
lamindb_setup/core/types.py,sha256=bcYnZ0uM_2NXKJCl94Mmc-uYrQlRUUVKG3sK2N-F-N4,532
|
|
39
|
-
lamindb_setup/core/upath.py,sha256=
|
|
40
|
-
lamindb_setup-0.
|
|
41
|
-
lamindb_setup-0.
|
|
42
|
-
lamindb_setup-0.
|
|
43
|
-
lamindb_setup-0.
|
|
40
|
+
lamindb_setup/core/upath.py,sha256=QnAiaOZgT1TLUaX0PEs9dSJ0E4ZDD431hCfKrJIbmqQ,26339
|
|
41
|
+
lamindb_setup-0.72.0.dist-info/LICENSE,sha256=UOZ1F5fFDe3XXvG4oNnkL1-Ecun7zpHzRxjp-XsMeAo,11324
|
|
42
|
+
lamindb_setup-0.72.0.dist-info/WHEEL,sha256=Sgu64hAMa6g5FdzHxXv9Xdse9yxpGGMeagVtPMWpJQY,99
|
|
43
|
+
lamindb_setup-0.72.0.dist-info/METADATA,sha256=8Bp06EbsyZPsRjXGPGb0Le95RfKbQXlMsf-4sFUgESM,1620
|
|
44
|
+
lamindb_setup-0.72.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|