lamindb_setup 0.70.0__py2.py3-none-any.whl → 0.71.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 +15 -15
- lamindb_setup/_add_remote_storage.py +22 -33
- lamindb_setup/_cache.py +4 -1
- lamindb_setup/_check.py +3 -0
- lamindb_setup/_check_setup.py +13 -7
- lamindb_setup/_close.py +2 -0
- lamindb_setup/_connect_instance.py +33 -26
- lamindb_setup/_delete.py +52 -19
- lamindb_setup/_django.py +4 -1
- lamindb_setup/_exportdb.py +4 -2
- lamindb_setup/_importdb.py +5 -1
- lamindb_setup/_init_instance.py +57 -45
- lamindb_setup/_migrate.py +16 -13
- lamindb_setup/_register_instance.py +10 -3
- lamindb_setup/_schema.py +6 -3
- lamindb_setup/_setup_user.py +7 -7
- lamindb_setup/_silence_loggers.py +4 -2
- lamindb_setup/core/__init__.py +4 -3
- lamindb_setup/core/_aws_storage.py +3 -0
- lamindb_setup/core/_deprecated.py +2 -7
- lamindb_setup/core/_docs.py +2 -0
- lamindb_setup/core/_hub_client.py +12 -10
- lamindb_setup/core/_hub_core.py +198 -88
- lamindb_setup/core/_hub_crud.py +15 -11
- lamindb_setup/core/_hub_utils.py +11 -8
- lamindb_setup/core/_settings.py +23 -26
- lamindb_setup/core/_settings_instance.py +149 -81
- lamindb_setup/core/_settings_load.py +12 -7
- lamindb_setup/core/_settings_save.py +11 -8
- lamindb_setup/core/_settings_storage.py +83 -42
- lamindb_setup/core/_settings_store.py +3 -2
- lamindb_setup/core/_settings_user.py +10 -6
- lamindb_setup/core/_setup_bionty_sources.py +9 -2
- lamindb_setup/core/cloud_sqlite_locker.py +13 -10
- lamindb_setup/core/django.py +3 -1
- lamindb_setup/core/exceptions.py +4 -2
- lamindb_setup/core/hashing.py +15 -5
- lamindb_setup/core/types.py +5 -2
- lamindb_setup/core/upath.py +181 -87
- {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.0.dist-info}/METADATA +6 -4
- lamindb_setup-0.71.0.dist-info/RECORD +43 -0
- lamindb_setup-0.70.0.dist-info/RECORD +0 -43
- {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.0.dist-info}/LICENSE +0 -0
- {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.0.dist-info}/WHEEL +0 -0
|
@@ -2,27 +2,25 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
4
|
import shutil
|
|
5
|
-
from .upath import LocalPathClasses, convert_pathlike
|
|
6
5
|
from pathlib import Path
|
|
7
|
-
from typing import
|
|
8
|
-
|
|
6
|
+
from typing import TYPE_CHECKING, Literal
|
|
7
|
+
|
|
8
|
+
from lamin_utils import logger
|
|
9
|
+
|
|
9
10
|
from ._hub_client import call_with_fallback
|
|
10
11
|
from ._hub_crud import select_account_handle_name_by_lnid
|
|
11
|
-
from
|
|
12
|
+
from ._hub_utils import LaminDsn, LaminDsnModel
|
|
13
|
+
from ._settings_save import save_instance_settings
|
|
14
|
+
from ._settings_storage import StorageSettings, init_storage, mark_storage_root
|
|
15
|
+
from ._settings_store import current_instance_settings_file, instance_settings_file
|
|
12
16
|
from .cloud_sqlite_locker import (
|
|
13
|
-
InstanceLockedException,
|
|
14
17
|
EXPIRATION_TIME,
|
|
18
|
+
InstanceLockedException,
|
|
15
19
|
)
|
|
16
|
-
from .
|
|
17
|
-
from ._settings_save import save_instance_settings
|
|
18
|
-
from ._settings_storage import StorageSettings
|
|
19
|
-
from ._settings_store import current_instance_settings_file, instance_settings_file
|
|
20
|
-
from .upath import UPath
|
|
20
|
+
from .upath import LocalPathClasses, UPath, convert_pathlike
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
" local_root`"
|
|
25
|
-
)
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from uuid import UUID
|
|
26
24
|
|
|
27
25
|
|
|
28
26
|
def sanitize_git_repo_url(repo_url: str) -> str:
|
|
@@ -39,26 +37,28 @@ class InstanceSettings:
|
|
|
39
37
|
owner: str, # owner handle
|
|
40
38
|
name: str, # instance name
|
|
41
39
|
storage: StorageSettings, # storage location
|
|
42
|
-
|
|
43
|
-
uid:
|
|
44
|
-
db:
|
|
45
|
-
schema:
|
|
46
|
-
git_repo:
|
|
40
|
+
keep_artifacts_local: bool = False, # default to local storage
|
|
41
|
+
uid: str | None = None, # instance uid/lnid
|
|
42
|
+
db: str | None = None, # DB URI
|
|
43
|
+
schema: str | None = None, # comma-separated string of schema names
|
|
44
|
+
git_repo: str | None = None, # a git repo URL
|
|
45
|
+
is_on_hub: bool = False, # initialized from hub
|
|
47
46
|
):
|
|
48
47
|
from ._hub_utils import validate_db_arg
|
|
49
48
|
|
|
50
|
-
self.
|
|
49
|
+
self._id_: UUID = id
|
|
51
50
|
self._owner: str = owner
|
|
52
51
|
self._name: str = name
|
|
53
|
-
self._uid:
|
|
52
|
+
self._uid: str | None = uid
|
|
54
53
|
self._storage: StorageSettings = storage
|
|
55
54
|
validate_db_arg(db)
|
|
56
|
-
self._db:
|
|
57
|
-
self._schema_str:
|
|
55
|
+
self._db: str | None = db
|
|
56
|
+
self._schema_str: str | None = schema
|
|
58
57
|
self._git_repo = None if git_repo is None else sanitize_git_repo_url(git_repo)
|
|
59
58
|
# local storage
|
|
60
|
-
self.
|
|
61
|
-
self.
|
|
59
|
+
self._keep_artifacts_local = keep_artifacts_local
|
|
60
|
+
self._storage_local: StorageSettings | None = None
|
|
61
|
+
self._is_on_hub = is_on_hub
|
|
62
62
|
|
|
63
63
|
def __repr__(self):
|
|
64
64
|
"""Rich string representation."""
|
|
@@ -97,60 +97,111 @@ class InstanceSettings:
|
|
|
97
97
|
"""Instance name."""
|
|
98
98
|
return self._name
|
|
99
99
|
|
|
100
|
-
def _search_local_root(
|
|
100
|
+
def _search_local_root(
|
|
101
|
+
self, local_root: str | None = None, mute_warning: bool = False
|
|
102
|
+
) -> StorageSettings | None:
|
|
101
103
|
from lnschema_core.models import Storage
|
|
102
104
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
if local_root is not None:
|
|
106
|
+
local_records = Storage.objects.filter(root=local_root)
|
|
107
|
+
else:
|
|
108
|
+
local_records = Storage.objects.filter(type="local")
|
|
109
|
+
found = False
|
|
110
|
+
for record in local_records.all():
|
|
111
|
+
root_path = Path(record.root)
|
|
112
|
+
if root_path.exists():
|
|
113
|
+
marker_path = root_path / ".lamindb/_is_initialized"
|
|
114
|
+
if marker_path.exists():
|
|
115
|
+
uid = marker_path.read_text()
|
|
116
|
+
if uid == record.uid:
|
|
117
|
+
found = True
|
|
118
|
+
break
|
|
119
|
+
elif uid == "":
|
|
120
|
+
# legacy instance that was not yet marked properly
|
|
121
|
+
mark_storage_root(record.root, record.uid)
|
|
122
|
+
else:
|
|
123
|
+
continue
|
|
124
|
+
else:
|
|
125
|
+
# legacy instance that was not yet marked at all
|
|
126
|
+
mark_storage_root(record.root, record.uid)
|
|
127
|
+
break
|
|
128
|
+
if found:
|
|
129
|
+
return StorageSettings(record.root)
|
|
130
|
+
elif not mute_warning:
|
|
131
|
+
logger.warning(
|
|
132
|
+
f"none of the registered local storage locations were found in your environment: {local_records}"
|
|
133
|
+
"\n❗ please register a new local storage location via `ln.settings.storage_local = local_root_path` "
|
|
134
|
+
"and re-load/connect the instance"
|
|
135
|
+
)
|
|
136
|
+
return None
|
|
111
137
|
|
|
112
138
|
@property
|
|
113
|
-
def
|
|
114
|
-
"""Default local
|
|
139
|
+
def keep_artifacts_local(self) -> bool:
|
|
140
|
+
"""Default to keeping artifacts local.
|
|
115
141
|
|
|
116
|
-
|
|
117
|
-
storage mode across local & cloud locations.
|
|
142
|
+
Enable this optional setting for cloud instances on lamin.ai.
|
|
118
143
|
|
|
119
|
-
|
|
120
|
-
|
|
144
|
+
Guide: :doc:`faq/keep-artifacts-local`
|
|
145
|
+
"""
|
|
146
|
+
return self._keep_artifacts_local
|
|
121
147
|
|
|
122
|
-
|
|
123
|
-
|
|
148
|
+
@property
|
|
149
|
+
def storage(self) -> StorageSettings:
|
|
150
|
+
"""Default storage.
|
|
124
151
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
:attr:`~lamindb.setup.core.InstanceSettings.storage`.
|
|
152
|
+
For a cloud instance, this is cloud storage. For a local instance, this
|
|
153
|
+
is a local directory.
|
|
128
154
|
"""
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if self.
|
|
144
|
-
raise
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
155
|
+
return self._storage
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def storage_local(self) -> StorageSettings:
|
|
159
|
+
"""An additional local default storage.
|
|
160
|
+
|
|
161
|
+
Is only available if :attr:`keep_artifacts_local` is enabled.
|
|
162
|
+
|
|
163
|
+
Guide: :doc:`faq/keep-artifacts-local`
|
|
164
|
+
"""
|
|
165
|
+
if not self._keep_artifacts_local:
|
|
166
|
+
raise ValueError("`keep_artifacts_local` is not enabled for this instance.")
|
|
167
|
+
if self._storage_local is None:
|
|
168
|
+
self._storage_local = self._search_local_root()
|
|
169
|
+
if self._storage_local is None:
|
|
170
|
+
# raise an error, there was a warning just before in search_local_root
|
|
171
|
+
raise ValueError()
|
|
172
|
+
return self._storage_local
|
|
173
|
+
|
|
174
|
+
@storage_local.setter
|
|
175
|
+
def storage_local(self, local_root: Path | str):
|
|
176
|
+
from lamindb_setup._init_instance import register_storage_in_instance
|
|
177
|
+
|
|
178
|
+
local_root = Path(local_root)
|
|
179
|
+
if not self._keep_artifacts_local:
|
|
180
|
+
raise ValueError("`keep_artifacts_local` is not enabled for this instance.")
|
|
181
|
+
storage_local = self._search_local_root(
|
|
182
|
+
local_root=StorageSettings(local_root).root_as_str, mute_warning=True
|
|
183
|
+
)
|
|
184
|
+
if storage_local is not None:
|
|
185
|
+
# great, we're merely switching storage location
|
|
186
|
+
self._storage_local = storage_local
|
|
187
|
+
logger.important(f"defaulting to local storage: {storage_local.root}")
|
|
188
|
+
return None
|
|
189
|
+
storage_local = self._search_local_root(mute_warning=True)
|
|
190
|
+
if storage_local is not None:
|
|
191
|
+
if os.getenv("LAMIN_TESTING") == "true":
|
|
192
|
+
response = "y"
|
|
193
|
+
else:
|
|
194
|
+
response = input(
|
|
195
|
+
"You already configured a local storage root for this instance in this"
|
|
196
|
+
f" environment: {self.storage_local.root}\nDo you want to register another one? (y/n)"
|
|
197
|
+
)
|
|
198
|
+
if response != "y":
|
|
199
|
+
return None
|
|
148
200
|
local_root = convert_pathlike(local_root)
|
|
149
201
|
assert isinstance(local_root, LocalPathClasses)
|
|
150
|
-
self.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
update_instance_record(self.id, {"storage_mode": "hybrid"})
|
|
202
|
+
self._storage_local = init_storage(local_root, self._id, register_hub=True) # type: ignore
|
|
203
|
+
register_storage_in_instance(self._storage_local) # type: ignore
|
|
204
|
+
logger.important(f"defaulting to local storage: {self._storage_local.root}")
|
|
154
205
|
|
|
155
206
|
@property
|
|
156
207
|
def slug(self) -> str:
|
|
@@ -158,7 +209,7 @@ class InstanceSettings:
|
|
|
158
209
|
return f"{self.owner}/{self.name}"
|
|
159
210
|
|
|
160
211
|
@property
|
|
161
|
-
def git_repo(self) ->
|
|
212
|
+
def git_repo(self) -> str | None:
|
|
162
213
|
"""Sync transforms with scripts in git repository.
|
|
163
214
|
|
|
164
215
|
Provide the full git repo URL.
|
|
@@ -166,17 +217,19 @@ class InstanceSettings:
|
|
|
166
217
|
return self._git_repo
|
|
167
218
|
|
|
168
219
|
@property
|
|
169
|
-
def
|
|
220
|
+
def _id(self) -> UUID:
|
|
170
221
|
"""The internal instance id."""
|
|
171
|
-
return self.
|
|
222
|
+
return self._id_
|
|
172
223
|
|
|
173
224
|
@property
|
|
174
|
-
def uid(self) ->
|
|
225
|
+
def uid(self) -> str:
|
|
175
226
|
"""The user-facing instance id."""
|
|
176
|
-
|
|
227
|
+
from .hashing import hash_and_encode_as_b62
|
|
228
|
+
|
|
229
|
+
return hash_and_encode_as_b62(self._id.hex)[:12]
|
|
177
230
|
|
|
178
231
|
@property
|
|
179
|
-
def schema(self) ->
|
|
232
|
+
def schema(self) -> set[str]:
|
|
180
233
|
"""Schema modules in addition to core schema."""
|
|
181
234
|
if self._schema_str is None:
|
|
182
235
|
return {} # type: ignore
|
|
@@ -186,7 +239,7 @@ class InstanceSettings:
|
|
|
186
239
|
@property
|
|
187
240
|
def _sqlite_file(self) -> UPath:
|
|
188
241
|
"""SQLite file."""
|
|
189
|
-
return self.storage.key_to_filepath(f"{self.
|
|
242
|
+
return self.storage.key_to_filepath(f"{self._id.hex}.lndb")
|
|
190
243
|
|
|
191
244
|
@property
|
|
192
245
|
def _sqlite_file_local(self) -> Path:
|
|
@@ -290,11 +343,6 @@ class InstanceSettings:
|
|
|
290
343
|
else:
|
|
291
344
|
return empty_locker
|
|
292
345
|
|
|
293
|
-
@property
|
|
294
|
-
def storage(self) -> StorageSettings:
|
|
295
|
-
"""Low-level access to storage location."""
|
|
296
|
-
return self._storage
|
|
297
|
-
|
|
298
346
|
@property
|
|
299
347
|
def is_remote(self) -> bool:
|
|
300
348
|
"""Boolean indicating if an instance has no local component."""
|
|
@@ -317,6 +365,25 @@ class InstanceSettings:
|
|
|
317
365
|
# and remote postgres
|
|
318
366
|
return True
|
|
319
367
|
|
|
368
|
+
@property
|
|
369
|
+
def is_on_hub(self) -> bool:
|
|
370
|
+
"""Is this instance on the hub.
|
|
371
|
+
|
|
372
|
+
Only works if user has access to the instance.
|
|
373
|
+
"""
|
|
374
|
+
if self._is_on_hub is None:
|
|
375
|
+
from ._hub_client import call_with_fallback_auth
|
|
376
|
+
from ._hub_crud import select_instance_by_id
|
|
377
|
+
|
|
378
|
+
response = call_with_fallback_auth(
|
|
379
|
+
select_instance_by_id, instance_id=self._id.hex
|
|
380
|
+
)
|
|
381
|
+
if response is None:
|
|
382
|
+
self._is_on_hub = False
|
|
383
|
+
else:
|
|
384
|
+
self._is_on_hub = True
|
|
385
|
+
return self._is_on_hub
|
|
386
|
+
|
|
320
387
|
def _get_settings_file(self) -> Path:
|
|
321
388
|
return instance_settings_file(self.name, self.owner)
|
|
322
389
|
|
|
@@ -341,7 +408,7 @@ class InstanceSettings:
|
|
|
341
408
|
|
|
342
409
|
def _load_db(
|
|
343
410
|
self, do_not_lock_for_laminapp_admin: bool = False
|
|
344
|
-
) ->
|
|
411
|
+
) -> tuple[bool, str]:
|
|
345
412
|
# Is the database available and initialized as LaminDB?
|
|
346
413
|
# returns a tuple of status code and message
|
|
347
414
|
if self.dialect == "sqlite" and not self._sqlite_file.exists():
|
|
@@ -353,6 +420,7 @@ class InstanceSettings:
|
|
|
353
420
|
)
|
|
354
421
|
return False, f"SQLite file {self._sqlite_file} does not exist"
|
|
355
422
|
from lamindb_setup import settings # to check user
|
|
423
|
+
|
|
356
424
|
from .django import setup_django
|
|
357
425
|
|
|
358
426
|
# lock in all cases except if do_not_lock_for_laminapp_admin is True and
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
3
4
|
from uuid import UUID, uuid4
|
|
5
|
+
|
|
4
6
|
from lamin_utils import logger
|
|
5
7
|
from pydantic.error_wrappers import ValidationError
|
|
6
8
|
|
|
@@ -14,12 +16,15 @@ from ._settings_store import (
|
|
|
14
16
|
)
|
|
15
17
|
from ._settings_user import UserSettings
|
|
16
18
|
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
|
|
17
22
|
|
|
18
23
|
class SettingsEnvFileOutdated(Exception):
|
|
19
24
|
pass
|
|
20
25
|
|
|
21
26
|
|
|
22
|
-
def load_instance_settings(instance_settings_file:
|
|
27
|
+
def load_instance_settings(instance_settings_file: Path | None = None):
|
|
23
28
|
if instance_settings_file is None:
|
|
24
29
|
instance_settings_file = current_instance_settings_file()
|
|
25
30
|
if not instance_settings_file.exists():
|
|
@@ -33,10 +38,10 @@ def load_instance_settings(instance_settings_file: Optional[Path] = None):
|
|
|
33
38
|
f"\n\n{error}\n\nYour instance settings file with\n\n{content}\nis invalid"
|
|
34
39
|
f" (likely outdated), please delete {instance_settings_file} &"
|
|
35
40
|
" re-initialize (local) or re-connect to the instance (remote)"
|
|
36
|
-
)
|
|
41
|
+
) from error
|
|
37
42
|
if settings_store.id == "null":
|
|
38
43
|
raise ValueError(
|
|
39
|
-
"Your instance.
|
|
44
|
+
"Your instance._id is undefined, please either load your instance from the"
|
|
40
45
|
f" hub or update {instance_settings_file} with a new id: {uuid4().hex}"
|
|
41
46
|
)
|
|
42
47
|
isettings = setup_instance_from_store(settings_store)
|
|
@@ -60,13 +65,13 @@ def load_or_create_user_settings() -> UserSettings:
|
|
|
60
65
|
def load_user_settings(user_settings_file: Path):
|
|
61
66
|
try:
|
|
62
67
|
settings_store = UserSettingsStore(_env_file=user_settings_file)
|
|
63
|
-
except (ValidationError, TypeError):
|
|
68
|
+
except (ValidationError, TypeError) as error:
|
|
64
69
|
msg = (
|
|
65
70
|
"Your user settings file is invalid, please delete"
|
|
66
71
|
f" {user_settings_file} and log in again."
|
|
67
72
|
)
|
|
68
73
|
print(msg)
|
|
69
|
-
raise SettingsEnvFileOutdated(msg)
|
|
74
|
+
raise SettingsEnvFileOutdated(msg) from error
|
|
70
75
|
settings = setup_user_from_store(settings_store)
|
|
71
76
|
return settings
|
|
72
77
|
|
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Optional, get_type_hints
|
|
4
5
|
from uuid import UUID
|
|
5
6
|
|
|
6
7
|
from ._settings_store import (
|
|
7
|
-
UserSettingsStore,
|
|
8
8
|
InstanceSettingsStore,
|
|
9
|
+
UserSettingsStore,
|
|
9
10
|
current_user_settings_file,
|
|
10
11
|
user_settings_file_email,
|
|
11
12
|
user_settings_file_handle,
|
|
12
13
|
)
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from ._settings_user import UserSettings
|
|
17
|
+
from .upath import UPath
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
def save_user_settings(settings: UserSettings):
|
|
@@ -31,7 +34,7 @@ def save_user_settings(settings: UserSettings):
|
|
|
31
34
|
def save_settings(
|
|
32
35
|
settings: Any,
|
|
33
36
|
settings_file: Path,
|
|
34
|
-
type_hints:
|
|
37
|
+
type_hints: dict[str, Any],
|
|
35
38
|
prefix: str,
|
|
36
39
|
):
|
|
37
40
|
with open(settings_file, "w") as f:
|
|
@@ -44,7 +47,7 @@ def save_settings(
|
|
|
44
47
|
elif store_key == "storage_region":
|
|
45
48
|
value = settings.storage.region
|
|
46
49
|
else:
|
|
47
|
-
if store_key in {"db", "schema_str", "name_", "uuid"}:
|
|
50
|
+
if store_key in {"db", "schema_str", "name_", "uuid", "id"}:
|
|
48
51
|
settings_key = f"_{store_key.rstrip('_')}"
|
|
49
52
|
else:
|
|
50
53
|
settings_key = store_key
|
|
@@ -65,7 +68,7 @@ def save_instance_settings(settings: Any, settings_file: Path):
|
|
|
65
68
|
|
|
66
69
|
|
|
67
70
|
def save_system_storage_settings(
|
|
68
|
-
cache_path:
|
|
71
|
+
cache_path: str | Path | UPath | None, settings_file: Path
|
|
69
72
|
):
|
|
70
73
|
cache_path = "null" if cache_path is None else cache_path
|
|
71
74
|
if isinstance(cache_path, Path): # also True for UPath
|