lamindb_setup 1.18.2__py3-none-any.whl → 1.19.1__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 +4 -19
- lamindb_setup/_cache.py +87 -87
- lamindb_setup/_check.py +7 -7
- lamindb_setup/_check_setup.py +131 -131
- lamindb_setup/_connect_instance.py +443 -438
- lamindb_setup/_delete.py +155 -151
- lamindb_setup/_disconnect.py +38 -38
- lamindb_setup/_django.py +39 -39
- lamindb_setup/_entry_points.py +19 -19
- lamindb_setup/_init_instance.py +423 -429
- lamindb_setup/_migrate.py +331 -327
- lamindb_setup/_register_instance.py +32 -32
- lamindb_setup/_schema.py +27 -27
- lamindb_setup/_schema_metadata.py +451 -451
- lamindb_setup/_set_managed_storage.py +81 -80
- lamindb_setup/_setup_user.py +198 -198
- lamindb_setup/_silence_loggers.py +46 -46
- lamindb_setup/core/__init__.py +25 -34
- lamindb_setup/core/_aws_options.py +276 -266
- lamindb_setup/core/_aws_storage.py +57 -55
- lamindb_setup/core/_clone.py +50 -50
- lamindb_setup/core/_deprecated.py +62 -62
- lamindb_setup/core/_docs.py +14 -14
- lamindb_setup/core/_hub_client.py +288 -294
- lamindb_setup/core/_hub_core.py +0 -2
- lamindb_setup/core/_hub_crud.py +247 -247
- lamindb_setup/core/_hub_utils.py +100 -100
- lamindb_setup/core/_private_django_api.py +80 -80
- lamindb_setup/core/_settings.py +440 -434
- lamindb_setup/core/_settings_instance.py +32 -7
- lamindb_setup/core/_settings_load.py +162 -159
- lamindb_setup/core/_settings_save.py +108 -96
- lamindb_setup/core/_settings_storage.py +433 -433
- lamindb_setup/core/_settings_store.py +162 -92
- lamindb_setup/core/_settings_user.py +55 -55
- lamindb_setup/core/_setup_bionty_sources.py +44 -44
- lamindb_setup/core/cloud_sqlite_locker.py +240 -240
- lamindb_setup/core/django.py +414 -413
- lamindb_setup/core/exceptions.py +1 -1
- lamindb_setup/core/hashing.py +134 -134
- lamindb_setup/core/types.py +1 -1
- lamindb_setup/core/upath.py +1031 -1028
- lamindb_setup/errors.py +72 -70
- lamindb_setup/io.py +423 -416
- lamindb_setup/types.py +17 -17
- {lamindb_setup-1.18.2.dist-info → lamindb_setup-1.19.1.dist-info}/METADATA +4 -2
- lamindb_setup-1.19.1.dist-info/RECORD +51 -0
- {lamindb_setup-1.18.2.dist-info → lamindb_setup-1.19.1.dist-info}/WHEEL +1 -1
- {lamindb_setup-1.18.2.dist-info → lamindb_setup-1.19.1.dist-info/licenses}/LICENSE +201 -201
- lamindb_setup-1.18.2.dist-info/RECORD +0 -51
lamindb_setup/core/_settings.py
CHANGED
|
@@ -1,434 +1,440 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
import sys
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
from typing import TYPE_CHECKING
|
|
7
|
-
|
|
8
|
-
import jwt
|
|
9
|
-
from lamin_utils import logger
|
|
10
|
-
from platformdirs import user_cache_dir
|
|
11
|
-
|
|
12
|
-
from
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
from
|
|
28
|
-
|
|
29
|
-
from lamindb_setup.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
#
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
from lamindb import
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
branch_record
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
#
|
|
175
|
-
|
|
176
|
-
self.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
#
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
from lamindb import
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
space_record
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
#
|
|
224
|
-
|
|
225
|
-
self.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
self._user_settings_env
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
)
|
|
272
|
-
self.
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
self._instance_settings_env
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
self.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
""
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
cache_dir
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
#
|
|
314
|
-
#
|
|
315
|
-
#
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
def
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
if
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
repr += f"{
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
import jwt
|
|
9
|
+
from lamin_utils import logger
|
|
10
|
+
from platformdirs import user_cache_dir
|
|
11
|
+
|
|
12
|
+
from ._settings_load import (
|
|
13
|
+
load_cache_path_from_settings,
|
|
14
|
+
load_instance_settings,
|
|
15
|
+
load_or_create_user_settings,
|
|
16
|
+
)
|
|
17
|
+
from ._settings_store import (
|
|
18
|
+
current_instance_settings_file,
|
|
19
|
+
settings_dir,
|
|
20
|
+
system_settings_dir,
|
|
21
|
+
)
|
|
22
|
+
from .upath import LocalPathClasses, UPath
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from lamindb.models import Branch, Space
|
|
26
|
+
|
|
27
|
+
from lamindb_setup.core import InstanceSettings, StorageSettings, UserSettings
|
|
28
|
+
from lamindb_setup.core.django import DBToken, DBTokenManager
|
|
29
|
+
from lamindb_setup.types import UPathStr
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
DEFAULT_CACHE_DIR = UPath(user_cache_dir(appname="lamindb", appauthor="laminlabs"))
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _process_cache_path(cache_path: UPathStr | None) -> UPath | None:
|
|
36
|
+
if cache_path is None or cache_path == "null":
|
|
37
|
+
return None
|
|
38
|
+
cache_dir = UPath(cache_path)
|
|
39
|
+
if not isinstance(cache_dir, LocalPathClasses):
|
|
40
|
+
raise ValueError("cache dir should be a local path.")
|
|
41
|
+
if cache_dir.exists() and not cache_dir.is_dir():
|
|
42
|
+
raise ValueError("cache dir should be a directory.")
|
|
43
|
+
if not cache_dir.is_absolute():
|
|
44
|
+
raise ValueError("A path to the cache dir should be absolute.")
|
|
45
|
+
return cache_dir
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# returned by settings.branch for none/none instance
|
|
49
|
+
class MainBranchMock:
|
|
50
|
+
id = 1
|
|
51
|
+
name = "main"
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class SetupSettings:
|
|
55
|
+
"""Setup settings."""
|
|
56
|
+
|
|
57
|
+
_using_key: str | None = None # set through lamindb.settings
|
|
58
|
+
|
|
59
|
+
_user_settings: UserSettings | None = None
|
|
60
|
+
_instance_settings: InstanceSettings | None = None
|
|
61
|
+
|
|
62
|
+
_user_settings_env: str | None = None
|
|
63
|
+
_instance_settings_env: str | None = None
|
|
64
|
+
|
|
65
|
+
_auto_connect_path: Path = settings_dir / "auto_connect"
|
|
66
|
+
_private_django_api_path: Path = settings_dir / "private_django_api"
|
|
67
|
+
|
|
68
|
+
_cache_dir: Path | None = None
|
|
69
|
+
|
|
70
|
+
_branch = None # do not have types here
|
|
71
|
+
_space = None # do not have types here
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def _instance_settings_path(self) -> Path:
|
|
75
|
+
return current_instance_settings_file()
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def settings_dir(self) -> Path:
|
|
79
|
+
"""The directory that holds locally persisted settings."""
|
|
80
|
+
return settings_dir
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def auto_connect(self) -> bool:
|
|
84
|
+
"""Auto-connect to current instance upon `import lamindb`.
|
|
85
|
+
|
|
86
|
+
This setting is always `True` and will be removed in a future version.
|
|
87
|
+
"""
|
|
88
|
+
return True
|
|
89
|
+
|
|
90
|
+
@auto_connect.setter
|
|
91
|
+
def auto_connect(self, value: bool) -> None:
|
|
92
|
+
logger.warning(
|
|
93
|
+
"setting auto_connect to `False` no longer has an effect and the setting will likely be removed in the future",
|
|
94
|
+
)
|
|
95
|
+
if value:
|
|
96
|
+
self._auto_connect_path.touch()
|
|
97
|
+
else:
|
|
98
|
+
self._auto_connect_path.unlink(missing_ok=True)
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def _dev_dir_path(self) -> Path:
|
|
102
|
+
return (
|
|
103
|
+
settings_dir / f"dev-dir--{self.instance.owner}--{self.instance.name}.txt"
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def dev_dir(self) -> Path | None:
|
|
108
|
+
"""Get or set the local development directory for the current instance.
|
|
109
|
+
|
|
110
|
+
If setting it to `None`, the working development directory is unset.
|
|
111
|
+
"""
|
|
112
|
+
if not self._dev_dir_path.exists():
|
|
113
|
+
return None
|
|
114
|
+
return Path(self._dev_dir_path.read_text())
|
|
115
|
+
|
|
116
|
+
@dev_dir.setter
|
|
117
|
+
def dev_dir(self, value: str | Path | None) -> None:
|
|
118
|
+
if value is None:
|
|
119
|
+
if self._dev_dir_path.exists():
|
|
120
|
+
self._dev_dir_path.unlink()
|
|
121
|
+
else:
|
|
122
|
+
value_str = Path(value).expanduser().resolve().as_posix()
|
|
123
|
+
self._dev_dir_path.write_text(value_str)
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def _branch_path(self) -> Path:
|
|
127
|
+
return (
|
|
128
|
+
settings_dir
|
|
129
|
+
/ f"current-branch--{self.instance.owner}--{self.instance.name}.txt"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def _read_branch_idlike_name(self) -> tuple[int | str, str]:
|
|
133
|
+
idlike: str | int = 1
|
|
134
|
+
name: str = "main"
|
|
135
|
+
try:
|
|
136
|
+
branch_path = self._branch_path
|
|
137
|
+
except SystemExit: # in case no instance setup
|
|
138
|
+
return idlike, name
|
|
139
|
+
if branch_path.exists():
|
|
140
|
+
idlike, name = branch_path.read_text().split("\n")
|
|
141
|
+
return idlike, name
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
# TODO: refactor so that it returns a BranchMock object
|
|
145
|
+
# and we never need a DB request
|
|
146
|
+
def branch(self) -> Branch:
|
|
147
|
+
"""Default branch."""
|
|
148
|
+
# this is needed for .filter() with non-default connections
|
|
149
|
+
if not self._instance_exists:
|
|
150
|
+
return MainBranchMock()
|
|
151
|
+
|
|
152
|
+
if self._branch is None:
|
|
153
|
+
from lamindb import Branch
|
|
154
|
+
|
|
155
|
+
idlike, _ = self._read_branch_idlike_name()
|
|
156
|
+
self._branch = Branch.get(idlike)
|
|
157
|
+
return self._branch
|
|
158
|
+
|
|
159
|
+
@branch.setter
|
|
160
|
+
def branch(self, value: str | Branch) -> None:
|
|
161
|
+
from lamindb import Branch, Q
|
|
162
|
+
from lamindb.errors import DoesNotExist
|
|
163
|
+
|
|
164
|
+
if isinstance(value, Branch):
|
|
165
|
+
assert value._state.adding is False, "Branch must be saved"
|
|
166
|
+
branch_record = value
|
|
167
|
+
else:
|
|
168
|
+
branch_record = Branch.filter(Q(name=value) | Q(uid=value)).one_or_none()
|
|
169
|
+
if branch_record is None:
|
|
170
|
+
raise DoesNotExist(
|
|
171
|
+
f"Branch '{value}', please check on the hub UI whether you have the correct `uid` or `name`."
|
|
172
|
+
)
|
|
173
|
+
# we are sure that the current instance is setup because
|
|
174
|
+
# it will error on lamindb import otherwise
|
|
175
|
+
self._branch_path.write_text(f"{branch_record.uid}\n{branch_record.name}")
|
|
176
|
+
self._branch = branch_record
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def _space_path(self) -> Path:
|
|
180
|
+
return (
|
|
181
|
+
settings_dir
|
|
182
|
+
/ f"current-space--{self.instance.owner}--{self.instance.name}.txt"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
def _read_space_idlike_name(self) -> tuple[int | str, str]:
|
|
186
|
+
idlike: str | int = 1
|
|
187
|
+
name: str = "all"
|
|
188
|
+
try:
|
|
189
|
+
space_path = self._space_path
|
|
190
|
+
except SystemExit: # in case no instance setup
|
|
191
|
+
return idlike, name
|
|
192
|
+
if space_path.exists():
|
|
193
|
+
idlike, name = space_path.read_text().split("\n")
|
|
194
|
+
return idlike, name
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
# TODO: refactor so that it returns a BranchMock object
|
|
198
|
+
# and we never need a DB request
|
|
199
|
+
def space(self) -> Space:
|
|
200
|
+
"""Default space."""
|
|
201
|
+
if self._space is None:
|
|
202
|
+
from lamindb import Space
|
|
203
|
+
|
|
204
|
+
idlike, _ = self._read_space_idlike_name()
|
|
205
|
+
self._space = Space.get(idlike)
|
|
206
|
+
return self._space
|
|
207
|
+
|
|
208
|
+
@space.setter
|
|
209
|
+
def space(self, value: str | Space) -> None:
|
|
210
|
+
from lamindb import Q, Space
|
|
211
|
+
from lamindb.errors import DoesNotExist
|
|
212
|
+
|
|
213
|
+
if isinstance(value, Space):
|
|
214
|
+
assert value._state.adding is False, "Space must be saved"
|
|
215
|
+
space_record = value
|
|
216
|
+
else:
|
|
217
|
+
space_record = Space.filter(Q(name=value) | Q(uid=value)).one_or_none()
|
|
218
|
+
if space_record is None:
|
|
219
|
+
raise DoesNotExist(
|
|
220
|
+
f"Space '{value}', please check on the hub UI whether you have the correct `uid` or `name`."
|
|
221
|
+
)
|
|
222
|
+
# we are sure that the current instance is setup because
|
|
223
|
+
# it will error on lamindb import otherwise
|
|
224
|
+
self._space_path.write_text(f"{space_record.uid}\n{space_record.name}")
|
|
225
|
+
self._space = space_record
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def is_connected(self) -> bool:
|
|
229
|
+
"""Determine whether the current instance is fully connected and ready to use.
|
|
230
|
+
|
|
231
|
+
If `True`, the current instance is connected, meaning that the db and other settings
|
|
232
|
+
are properly configured for use.
|
|
233
|
+
"""
|
|
234
|
+
from . import django
|
|
235
|
+
|
|
236
|
+
return self._instance_exists and django.IS_SETUP
|
|
237
|
+
|
|
238
|
+
@property
|
|
239
|
+
def private_django_api(self) -> bool:
|
|
240
|
+
"""Turn internal Django API private to clean up the API (default `False`).
|
|
241
|
+
|
|
242
|
+
This patches your local pip-installed django installation.
|
|
243
|
+
You can undo the patch by setting this back to `False`.
|
|
244
|
+
"""
|
|
245
|
+
return self._private_django_api_path.exists()
|
|
246
|
+
|
|
247
|
+
@private_django_api.setter
|
|
248
|
+
def private_django_api(self, value: bool) -> None:
|
|
249
|
+
from ._private_django_api import private_django_api
|
|
250
|
+
|
|
251
|
+
# we don't want to call private_django_api() twice
|
|
252
|
+
if value and not self.private_django_api:
|
|
253
|
+
private_django_api()
|
|
254
|
+
self._private_django_api_path.touch()
|
|
255
|
+
elif not value and self.private_django_api:
|
|
256
|
+
private_django_api(reverse=True)
|
|
257
|
+
self._private_django_api_path.unlink(missing_ok=True)
|
|
258
|
+
|
|
259
|
+
@property
|
|
260
|
+
def user(self) -> UserSettings:
|
|
261
|
+
"""Settings of current user."""
|
|
262
|
+
env_changed = (
|
|
263
|
+
self._user_settings_env is not None
|
|
264
|
+
and self._user_settings_env != get_env_name()
|
|
265
|
+
)
|
|
266
|
+
if self._user_settings is None or env_changed:
|
|
267
|
+
# only uses LAMIN_API_KEY if there is no current_user.env
|
|
268
|
+
self._user_settings = load_or_create_user_settings(
|
|
269
|
+
api_key=os.environ.get("LAMIN_API_KEY")
|
|
270
|
+
)
|
|
271
|
+
self._user_settings_env = get_env_name()
|
|
272
|
+
if self._user_settings and self._user_settings.uid is None:
|
|
273
|
+
raise RuntimeError("Need to login, first: lamin login")
|
|
274
|
+
return self._user_settings # type: ignore
|
|
275
|
+
|
|
276
|
+
@property
|
|
277
|
+
def instance(self) -> InstanceSettings:
|
|
278
|
+
"""Settings of current LaminDB instance."""
|
|
279
|
+
env_changed = (
|
|
280
|
+
self._instance_settings_env is not None
|
|
281
|
+
and self._instance_settings_env != get_env_name()
|
|
282
|
+
)
|
|
283
|
+
if self._instance_settings is None or env_changed:
|
|
284
|
+
self._instance_settings = load_instance_settings()
|
|
285
|
+
self._instance_settings_env = get_env_name()
|
|
286
|
+
return self._instance_settings # type: ignore
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def storage(self) -> StorageSettings:
|
|
290
|
+
"""Settings of default storage."""
|
|
291
|
+
return self.instance.storage
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def _instance_exists(self):
|
|
295
|
+
return self.instance.slug != "none/none"
|
|
296
|
+
|
|
297
|
+
@property
|
|
298
|
+
def cache_dir(self) -> UPath:
|
|
299
|
+
"""Cache root, a local directory to cache cloud files."""
|
|
300
|
+
if "LAMIN_CACHE_DIR" in os.environ:
|
|
301
|
+
cache_dir = UPath(os.environ["LAMIN_CACHE_DIR"])
|
|
302
|
+
elif self._cache_dir is None:
|
|
303
|
+
cache_path = load_cache_path_from_settings()
|
|
304
|
+
cache_dir = _process_cache_path(cache_path)
|
|
305
|
+
if cache_dir is None:
|
|
306
|
+
cache_dir = DEFAULT_CACHE_DIR
|
|
307
|
+
self._cache_dir = cache_dir
|
|
308
|
+
else:
|
|
309
|
+
cache_dir = self._cache_dir
|
|
310
|
+
try:
|
|
311
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
312
|
+
# we don not want this to error
|
|
313
|
+
# beause no actual writing happens on just getting the cache dir
|
|
314
|
+
# in cloud_to_local_no_update for example
|
|
315
|
+
# so it should not fail on read-only systems
|
|
316
|
+
except Exception as e:
|
|
317
|
+
logger.warning(
|
|
318
|
+
f"Failed to create lamin cache directory at {cache_dir}: {e}"
|
|
319
|
+
)
|
|
320
|
+
return cache_dir
|
|
321
|
+
|
|
322
|
+
@property
|
|
323
|
+
def paths(self) -> type[SetupPaths]:
|
|
324
|
+
"""Convert cloud paths to lamindb local paths.
|
|
325
|
+
|
|
326
|
+
Use `settings.paths.cloud_to_local_no_update` or `settings.paths.cloud_to_local`.
|
|
327
|
+
"""
|
|
328
|
+
return SetupPaths
|
|
329
|
+
|
|
330
|
+
@property
|
|
331
|
+
def _db_token_manager(self) -> DBTokenManager:
|
|
332
|
+
from lamindb_setup.core.django import db_token_manager
|
|
333
|
+
|
|
334
|
+
return db_token_manager
|
|
335
|
+
|
|
336
|
+
def _get_db_token(self, connection_name: str = "default") -> DBToken | None:
|
|
337
|
+
return self._db_token_manager.tokens.get(connection_name, None)
|
|
338
|
+
|
|
339
|
+
def _debug_db_access(self):
|
|
340
|
+
"""Debug database access problems."""
|
|
341
|
+
instance = self.instance
|
|
342
|
+
db_permissions = instance._db_permissions
|
|
343
|
+
print("db connection: ", instance.db)
|
|
344
|
+
print("db permissions: ", db_permissions)
|
|
345
|
+
if db_permissions != "jwt":
|
|
346
|
+
return
|
|
347
|
+
# sets the token if not present yet
|
|
348
|
+
print("available spaces: ", instance.available_spaces)
|
|
349
|
+
|
|
350
|
+
tokens = self._db_token_manager.tokens
|
|
351
|
+
if tokens:
|
|
352
|
+
for conn, token in tokens.items():
|
|
353
|
+
token_encoded = token._token
|
|
354
|
+
if token_encoded is None:
|
|
355
|
+
token._refresh_token()
|
|
356
|
+
token_encoded = token._token
|
|
357
|
+
token_decoded = jwt.decode(
|
|
358
|
+
token_encoded, options={"verify_signature": False}
|
|
359
|
+
)
|
|
360
|
+
print(
|
|
361
|
+
f"db token for the connection '{conn}' is '{token_encoded}': {token_decoded}"
|
|
362
|
+
)
|
|
363
|
+
else:
|
|
364
|
+
print("no db tokens are present")
|
|
365
|
+
|
|
366
|
+
def __repr__(self) -> str:
|
|
367
|
+
"""Rich string representation."""
|
|
368
|
+
from lamin_utils import colors
|
|
369
|
+
|
|
370
|
+
# do not show current setting representation when building docs
|
|
371
|
+
if "sphinx" in sys.modules:
|
|
372
|
+
return object.__repr__(self)
|
|
373
|
+
|
|
374
|
+
repr = ""
|
|
375
|
+
if self._instance_exists:
|
|
376
|
+
instance_rep = self.instance.__repr__().split("\n")
|
|
377
|
+
repr += f"{colors.cyan('Instance:')} {instance_rep[0].replace('Instance: ', '')}\n"
|
|
378
|
+
repr += f" - branch: {self._read_branch_idlike_name()[1]}\n"
|
|
379
|
+
repr += f" - space: {self._read_space_idlike_name()[1]}\n"
|
|
380
|
+
repr += f" - dev-dir: {self.dev_dir}"
|
|
381
|
+
repr += f"\n{colors.yellow('Details:')}\n"
|
|
382
|
+
repr += "\n".join(instance_rep[1:])
|
|
383
|
+
else:
|
|
384
|
+
repr += f"{colors.cyan('Instance:')} None"
|
|
385
|
+
repr += f"\n{colors.blue('Cache & settings:')}\n"
|
|
386
|
+
repr += f" - cache: {self.cache_dir.as_posix()}\n"
|
|
387
|
+
repr += f" - user settings: {settings_dir.as_posix()}\n"
|
|
388
|
+
repr += f" - system settings: {system_settings_dir.as_posix()}"
|
|
389
|
+
repr += f"\n{colors.green('User:')} {self.user.handle}"
|
|
390
|
+
return repr
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
class SetupPaths:
|
|
394
|
+
"""A static class for conversion of cloud paths to lamindb local paths."""
|
|
395
|
+
|
|
396
|
+
@staticmethod
|
|
397
|
+
def cloud_to_local_no_update(
|
|
398
|
+
filepath: UPathStr, cache_key: str | None = None
|
|
399
|
+
) -> UPath:
|
|
400
|
+
"""Local (or local cache) filepath from filepath without synchronization."""
|
|
401
|
+
if not isinstance(filepath, UPath):
|
|
402
|
+
filepath = UPath(filepath)
|
|
403
|
+
# cache_key is ignored if filepath is a local path
|
|
404
|
+
if not isinstance(filepath, LocalPathClasses):
|
|
405
|
+
# settings is defined further in this file
|
|
406
|
+
if cache_key is None:
|
|
407
|
+
local_key = filepath.path # type: ignore
|
|
408
|
+
protocol = filepath.protocol # type: ignore
|
|
409
|
+
if protocol in {"http", "https"}:
|
|
410
|
+
local_key = local_key.removeprefix(protocol + "://")
|
|
411
|
+
else:
|
|
412
|
+
local_key = cache_key
|
|
413
|
+
local_filepath = settings.cache_dir / local_key
|
|
414
|
+
else:
|
|
415
|
+
local_filepath = filepath
|
|
416
|
+
return local_filepath
|
|
417
|
+
|
|
418
|
+
@staticmethod
|
|
419
|
+
def cloud_to_local(
|
|
420
|
+
filepath: UPathStr, cache_key: str | None = None, **kwargs
|
|
421
|
+
) -> UPath:
|
|
422
|
+
"""Local (or local cache) filepath from filepath."""
|
|
423
|
+
if not isinstance(filepath, UPath):
|
|
424
|
+
filepath = UPath(filepath)
|
|
425
|
+
# cache_key is ignored in cloud_to_local_no_update if filepath is local
|
|
426
|
+
local_filepath = SetupPaths.cloud_to_local_no_update(filepath, cache_key)
|
|
427
|
+
if not isinstance(filepath, LocalPathClasses):
|
|
428
|
+
local_filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
429
|
+
filepath.synchronize_to(local_filepath, **kwargs) # type: ignore
|
|
430
|
+
return local_filepath
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def get_env_name():
|
|
434
|
+
if "LAMIN_ENV" in os.environ:
|
|
435
|
+
return os.environ["LAMIN_ENV"]
|
|
436
|
+
else:
|
|
437
|
+
return "prod"
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
settings = SetupSettings()
|