lamindb_setup 1.9.1__py3-none-any.whl → 1.10.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 +107 -107
- lamindb_setup/_cache.py +87 -87
- lamindb_setup/_check_setup.py +192 -166
- lamindb_setup/_connect_instance.py +415 -328
- lamindb_setup/_delete.py +144 -141
- lamindb_setup/_disconnect.py +35 -32
- lamindb_setup/_init_instance.py +430 -440
- lamindb_setup/_migrate.py +278 -266
- lamindb_setup/_register_instance.py +32 -35
- lamindb_setup/_schema_metadata.py +441 -441
- lamindb_setup/_set_managed_storage.py +69 -70
- lamindb_setup/_setup_user.py +172 -133
- lamindb_setup/core/__init__.py +21 -21
- lamindb_setup/core/_aws_options.py +223 -223
- lamindb_setup/core/_aws_storage.py +9 -1
- lamindb_setup/core/_hub_client.py +248 -248
- lamindb_setup/core/_hub_core.py +728 -665
- lamindb_setup/core/_hub_crud.py +227 -227
- lamindb_setup/core/_private_django_api.py +83 -83
- lamindb_setup/core/_settings.py +384 -377
- lamindb_setup/core/_settings_instance.py +577 -569
- lamindb_setup/core/_settings_load.py +141 -141
- lamindb_setup/core/_settings_save.py +95 -95
- lamindb_setup/core/_settings_storage.py +427 -429
- lamindb_setup/core/_settings_store.py +91 -91
- 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 +315 -305
- 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 +1013 -1013
- lamindb_setup/errors.py +80 -70
- lamindb_setup/types.py +20 -20
- {lamindb_setup-1.9.1.dist-info → lamindb_setup-1.10.0.dist-info}/METADATA +3 -3
- lamindb_setup-1.10.0.dist-info/RECORD +50 -0
- lamindb_setup-1.9.1.dist-info/RECORD +0 -50
- {lamindb_setup-1.9.1.dist-info → lamindb_setup-1.10.0.dist-info}/LICENSE +0 -0
- {lamindb_setup-1.9.1.dist-info → lamindb_setup-1.10.0.dist-info}/WHEEL +0 -0
|
@@ -1,328 +1,415 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import importlib
|
|
4
|
-
import os
|
|
5
|
-
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from .
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
16
|
-
from .
|
|
17
|
-
from .
|
|
18
|
-
from .
|
|
19
|
-
from .core.
|
|
20
|
-
from .core.
|
|
21
|
-
from .core.
|
|
22
|
-
from .core.
|
|
23
|
-
from .core.
|
|
24
|
-
from .core.
|
|
25
|
-
from .
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
from
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
"
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
and db_dsn_hub.
|
|
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
|
-
make_hub_request =
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
#
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
if not
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
from uuid import UUID
|
|
8
|
+
|
|
9
|
+
from lamin_utils import logger
|
|
10
|
+
|
|
11
|
+
from ._check_setup import (
|
|
12
|
+
_check_instance_setup,
|
|
13
|
+
_get_current_instance_settings,
|
|
14
|
+
find_module_candidates,
|
|
15
|
+
)
|
|
16
|
+
from ._disconnect import disconnect
|
|
17
|
+
from ._init_instance import load_from_isettings
|
|
18
|
+
from ._silence_loggers import silence_loggers
|
|
19
|
+
from .core._hub_core import connect_instance_hub
|
|
20
|
+
from .core._hub_utils import LaminDsnModel
|
|
21
|
+
from .core._settings import settings
|
|
22
|
+
from .core._settings_instance import InstanceSettings
|
|
23
|
+
from .core._settings_load import load_instance_settings
|
|
24
|
+
from .core._settings_storage import StorageSettings
|
|
25
|
+
from .core._settings_store import instance_settings_file, settings_dir
|
|
26
|
+
from .core.cloud_sqlite_locker import unlock_cloud_sqlite_upon_exception
|
|
27
|
+
from .errors import CannotSwitchDefaultInstance
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
from .core._settings_user import UserSettings
|
|
33
|
+
from .types import UPathStr
|
|
34
|
+
|
|
35
|
+
# this is for testing purposes only
|
|
36
|
+
# set to True only to test failed load
|
|
37
|
+
_TEST_FAILED_LOAD = False
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
INSTANCE_NOT_FOUND_MESSAGE = (
|
|
41
|
+
"'{owner}/{name}' not found:"
|
|
42
|
+
" '{hub_result}'\nCheck your permissions:"
|
|
43
|
+
" https://lamin.ai/{owner}/{name}"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class InstanceNotFoundError(SystemExit):
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def check_db_dsn_equal_up_to_credentials(db_dsn_hub, db_dsn_local):
|
|
52
|
+
return (
|
|
53
|
+
db_dsn_hub.scheme == db_dsn_local.scheme
|
|
54
|
+
and db_dsn_hub.host == db_dsn_local.host
|
|
55
|
+
and db_dsn_hub.database == db_dsn_local.database
|
|
56
|
+
and db_dsn_hub.port == db_dsn_local.port
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def update_db_using_local(
|
|
61
|
+
hub_instance_result: dict[str, str],
|
|
62
|
+
settings_file: Path,
|
|
63
|
+
db: str | None = None,
|
|
64
|
+
raise_permission_error=True,
|
|
65
|
+
) -> str | None:
|
|
66
|
+
db_updated = None
|
|
67
|
+
# check if postgres
|
|
68
|
+
if hub_instance_result["db_scheme"] == "postgresql":
|
|
69
|
+
if db is not None:
|
|
70
|
+
# use only the provided db if it is set
|
|
71
|
+
db_updated = db
|
|
72
|
+
elif (db_env := os.getenv("LAMINDB_INSTANCE_DB")) is not None:
|
|
73
|
+
logger.important("loading db URL from env variable LAMINDB_INSTANCE_DB")
|
|
74
|
+
# read directly from the environment
|
|
75
|
+
db_updated = db_env
|
|
76
|
+
else:
|
|
77
|
+
db_hub = hub_instance_result["db"]
|
|
78
|
+
db_dsn_hub = LaminDsnModel(db=db_hub)
|
|
79
|
+
# read from a cached settings file in case the hub result is inexistent
|
|
80
|
+
if db_dsn_hub.db.user in {None, "none"} and settings_file.exists():
|
|
81
|
+
isettings = load_instance_settings(settings_file)
|
|
82
|
+
db_updated = isettings.db
|
|
83
|
+
else:
|
|
84
|
+
# just take the default hub result and ensure there is actually a user
|
|
85
|
+
if (
|
|
86
|
+
db_dsn_hub.db.user in {None, "none"}
|
|
87
|
+
and db_dsn_hub.db.password in {None, "none"}
|
|
88
|
+
and raise_permission_error
|
|
89
|
+
):
|
|
90
|
+
raise PermissionError(
|
|
91
|
+
"No database access, please ask your admin to provide you with"
|
|
92
|
+
" a DB URL and pass it via --db <db_url>"
|
|
93
|
+
)
|
|
94
|
+
db_updated = db_hub
|
|
95
|
+
return db_updated
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _connect_instance(
|
|
99
|
+
owner: str,
|
|
100
|
+
name: str,
|
|
101
|
+
*,
|
|
102
|
+
db: str | None = None,
|
|
103
|
+
raise_permission_error: bool = True,
|
|
104
|
+
access_token: str | None = None,
|
|
105
|
+
) -> InstanceSettings:
|
|
106
|
+
settings_file = instance_settings_file(name, owner)
|
|
107
|
+
make_hub_request = True
|
|
108
|
+
if settings_file.exists():
|
|
109
|
+
isettings = load_instance_settings(settings_file)
|
|
110
|
+
# skip hub request for a purely local instance
|
|
111
|
+
if isettings.is_remote:
|
|
112
|
+
make_hub_request = True
|
|
113
|
+
else:
|
|
114
|
+
make_hub_request = False
|
|
115
|
+
if db is not None and isettings.dialect == "postgresql":
|
|
116
|
+
isettings._db = db
|
|
117
|
+
if make_hub_request:
|
|
118
|
+
# the following will return a string if the instance does not exist
|
|
119
|
+
# on the hub
|
|
120
|
+
# do not call hub if the user is anonymous
|
|
121
|
+
if owner != "anonymous":
|
|
122
|
+
hub_result = connect_instance_hub(
|
|
123
|
+
owner=owner, name=name, access_token=access_token
|
|
124
|
+
)
|
|
125
|
+
else:
|
|
126
|
+
hub_result = "anonymous-user"
|
|
127
|
+
# if hub_result is not a string, it means it made a request
|
|
128
|
+
# that successfully returned metadata
|
|
129
|
+
if not isinstance(hub_result, str):
|
|
130
|
+
instance_result, storage_result = hub_result
|
|
131
|
+
db_updated = update_db_using_local(
|
|
132
|
+
instance_result,
|
|
133
|
+
settings_file,
|
|
134
|
+
db=db,
|
|
135
|
+
raise_permission_error=raise_permission_error,
|
|
136
|
+
)
|
|
137
|
+
ssettings = StorageSettings(
|
|
138
|
+
root=storage_result["root"],
|
|
139
|
+
region=storage_result["region"],
|
|
140
|
+
uid=storage_result["lnid"],
|
|
141
|
+
uuid=UUID(storage_result["id"]),
|
|
142
|
+
instance_id=UUID(instance_result["id"]),
|
|
143
|
+
)
|
|
144
|
+
isettings = InstanceSettings(
|
|
145
|
+
id=UUID(instance_result["id"]),
|
|
146
|
+
owner=owner,
|
|
147
|
+
name=instance_result["name"],
|
|
148
|
+
storage=ssettings,
|
|
149
|
+
db=db_updated,
|
|
150
|
+
modules=instance_result["schema_str"],
|
|
151
|
+
git_repo=instance_result["git_repo"],
|
|
152
|
+
keep_artifacts_local=bool(instance_result["keep_artifacts_local"]),
|
|
153
|
+
is_on_hub=True,
|
|
154
|
+
api_url=instance_result["api_url"],
|
|
155
|
+
schema_id=None
|
|
156
|
+
if (schema_id := instance_result["schema_id"]) is None
|
|
157
|
+
else UUID(schema_id),
|
|
158
|
+
fine_grained_access=instance_result.get("fine_grained_access", False),
|
|
159
|
+
db_permissions=instance_result.get("db_permissions", None),
|
|
160
|
+
)
|
|
161
|
+
else:
|
|
162
|
+
if hub_result != "anonymous-user":
|
|
163
|
+
message = INSTANCE_NOT_FOUND_MESSAGE.format(
|
|
164
|
+
owner=owner, name=name, hub_result=hub_result
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
message = "It is not possible to load an anonymous-owned instance from the hub"
|
|
168
|
+
if settings_file.exists():
|
|
169
|
+
isettings = load_instance_settings(settings_file)
|
|
170
|
+
if isettings.is_remote:
|
|
171
|
+
raise InstanceNotFoundError(message)
|
|
172
|
+
else:
|
|
173
|
+
raise InstanceNotFoundError(message)
|
|
174
|
+
return isettings
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def reset_django_module_variables():
|
|
178
|
+
# This function updates all module-level references to Django classes
|
|
179
|
+
# But it will fail to update function level references
|
|
180
|
+
# So, if a user has
|
|
181
|
+
# def my_function():
|
|
182
|
+
# import lamindb as ln
|
|
183
|
+
# ...
|
|
184
|
+
#
|
|
185
|
+
# Then it will **not** work and the `ln` variable will become stale and hold a reference
|
|
186
|
+
# to the old classes
|
|
187
|
+
# There doesn't seem to be an easy way to fix this problem
|
|
188
|
+
|
|
189
|
+
import types
|
|
190
|
+
|
|
191
|
+
from django.apps import apps
|
|
192
|
+
|
|
193
|
+
app_names = {app.name for app in apps.get_app_configs()}
|
|
194
|
+
|
|
195
|
+
for name, module in sys.modules.items():
|
|
196
|
+
if (
|
|
197
|
+
module is not None
|
|
198
|
+
and (not name.startswith("__") or name == "__main__")
|
|
199
|
+
and name not in sys.builtin_module_names
|
|
200
|
+
and not (
|
|
201
|
+
hasattr(module, "__file__")
|
|
202
|
+
and module.__file__
|
|
203
|
+
and any(
|
|
204
|
+
path in module.__file__ for path in ["/lib/python", "\\lib\\python"]
|
|
205
|
+
)
|
|
206
|
+
)
|
|
207
|
+
):
|
|
208
|
+
try:
|
|
209
|
+
for k, v in vars(module).items():
|
|
210
|
+
if (
|
|
211
|
+
isinstance(v, types.ModuleType)
|
|
212
|
+
and not k.startswith("_")
|
|
213
|
+
and getattr(v, "__name__", None) in app_names
|
|
214
|
+
):
|
|
215
|
+
if v.__name__ in sys.modules:
|
|
216
|
+
vars(module)[k] = sys.modules[v.__name__]
|
|
217
|
+
# Also reset classes from Django apps - but check if the class module starts with any app name
|
|
218
|
+
elif hasattr(v, "__module__") and getattr(v, "__module__", None):
|
|
219
|
+
class_module = v.__module__
|
|
220
|
+
# Check if the class module starts with any of our app names
|
|
221
|
+
if any(
|
|
222
|
+
class_module.startswith(app_name) for app_name in app_names
|
|
223
|
+
):
|
|
224
|
+
if class_module in sys.modules:
|
|
225
|
+
fresh_module = sys.modules[class_module]
|
|
226
|
+
attr_name = getattr(v, "__name__", k)
|
|
227
|
+
if hasattr(fresh_module, attr_name):
|
|
228
|
+
vars(module)[k] = getattr(fresh_module, attr_name)
|
|
229
|
+
except (AttributeError, TypeError):
|
|
230
|
+
continue
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _connect_cli(instance: str) -> None:
|
|
234
|
+
from lamindb_setup import settings as settings_
|
|
235
|
+
|
|
236
|
+
settings_.auto_connect = True
|
|
237
|
+
owner, name = get_owner_name_from_identifier(instance)
|
|
238
|
+
isettings = _connect_instance(owner, name)
|
|
239
|
+
isettings._persist(write_to_disk=True)
|
|
240
|
+
if not isettings.is_on_hub or isettings._is_cloud_sqlite:
|
|
241
|
+
# there are two reasons to call the full-blown connect
|
|
242
|
+
# (1) if the instance is not on the hub, we need to register
|
|
243
|
+
# potential users through register_user()
|
|
244
|
+
# (2) if the instance is cloud sqlite, we need to lock it
|
|
245
|
+
connect(_write_settings=False, _reload_lamindb=False)
|
|
246
|
+
else:
|
|
247
|
+
logger.important(f"connected lamindb: {isettings.slug}")
|
|
248
|
+
return None
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@unlock_cloud_sqlite_upon_exception(ignore_prev_locker=True)
|
|
252
|
+
def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
|
|
253
|
+
"""Connect to an instance.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
instance: Pass a slug (`account/name`) or URL (`https://lamin.ai/account/name`).
|
|
257
|
+
If `None`, looks for an environment variable `LAMIN_CURRENT_INSTANCE` to get the instance identifier.
|
|
258
|
+
If it doesn't find this variable, it connects to the instance that was connected with `lamin connect` through the CLI.
|
|
259
|
+
|
|
260
|
+
See Also:
|
|
261
|
+
Configure an instance for auto-connect via the CLI, see `here <https://docs.lamin.ai/cli#connect>`__.
|
|
262
|
+
"""
|
|
263
|
+
# validate kwargs
|
|
264
|
+
valid_kwargs = {
|
|
265
|
+
"_db",
|
|
266
|
+
"_write_settings",
|
|
267
|
+
"_raise_not_found_error",
|
|
268
|
+
"_reload_lamindb",
|
|
269
|
+
"_test",
|
|
270
|
+
"_user",
|
|
271
|
+
}
|
|
272
|
+
for kwarg in kwargs:
|
|
273
|
+
if kwarg not in valid_kwargs:
|
|
274
|
+
raise TypeError(f"connect() got unexpected keyword argument '{kwarg}'")
|
|
275
|
+
isettings: InstanceSettings = None # type: ignore
|
|
276
|
+
# _db is still needed because it is called in init
|
|
277
|
+
_db: str | None = kwargs.get("_db", None)
|
|
278
|
+
_write_settings: bool = kwargs.get("_write_settings", False)
|
|
279
|
+
_raise_not_found_error: bool = kwargs.get("_raise_not_found_error", True)
|
|
280
|
+
_reload_lamindb: bool = kwargs.get("_reload_lamindb", True)
|
|
281
|
+
_test: bool = kwargs.get("_test", False)
|
|
282
|
+
|
|
283
|
+
access_token: str | None = None
|
|
284
|
+
_user: UserSettings | None = kwargs.get("_user", None)
|
|
285
|
+
if _user is not None:
|
|
286
|
+
access_token = _user.access_token
|
|
287
|
+
if instance is None:
|
|
288
|
+
instance = os.environ.get("LAMIN_CURRENT_INSTANCE")
|
|
289
|
+
|
|
290
|
+
try:
|
|
291
|
+
if instance is None:
|
|
292
|
+
isettings_or_none = _get_current_instance_settings()
|
|
293
|
+
if isettings_or_none is None:
|
|
294
|
+
raise ValueError(
|
|
295
|
+
"No instance was connected through the CLI, pass a value to `instance` or connect via the CLI."
|
|
296
|
+
)
|
|
297
|
+
isettings = isettings_or_none
|
|
298
|
+
if _db is not None and isettings.dialect == "postgresql":
|
|
299
|
+
isettings._db = _db
|
|
300
|
+
else:
|
|
301
|
+
from django.db import connection
|
|
302
|
+
|
|
303
|
+
owner, name = get_owner_name_from_identifier(instance)
|
|
304
|
+
if _check_instance_setup() and not _test:
|
|
305
|
+
if (
|
|
306
|
+
settings._instance_exists
|
|
307
|
+
and f"{owner}/{name}" == settings.instance.slug
|
|
308
|
+
# below is to ensure that if another process interferes
|
|
309
|
+
# we don't use the in-memory mock database
|
|
310
|
+
# could be made more specific by checking whether the django
|
|
311
|
+
# configured database is the same as the one in settings
|
|
312
|
+
and connection.settings_dict["NAME"] != ":memory:"
|
|
313
|
+
):
|
|
314
|
+
logger.important(
|
|
315
|
+
f"doing nothing, already connected lamindb: {settings.instance.slug}"
|
|
316
|
+
)
|
|
317
|
+
return None
|
|
318
|
+
else:
|
|
319
|
+
from lamindb_setup.core.django import reset_django
|
|
320
|
+
|
|
321
|
+
if (
|
|
322
|
+
settings._instance_exists
|
|
323
|
+
and settings.instance.slug != "none/none"
|
|
324
|
+
):
|
|
325
|
+
import lamindb as ln
|
|
326
|
+
|
|
327
|
+
if ln.context.transform is not None:
|
|
328
|
+
raise CannotSwitchDefaultInstance(
|
|
329
|
+
"Cannot switch default instance while `ln.track()` is live: call `ln.finish()`"
|
|
330
|
+
)
|
|
331
|
+
else:
|
|
332
|
+
logger.important_hint(
|
|
333
|
+
"switching the default lamindb instance might produce unexpected side effects with function-scoped imports: "
|
|
334
|
+
"please import lamindb at the module level instead of inside functions"
|
|
335
|
+
)
|
|
336
|
+
reset_django()
|
|
337
|
+
elif (
|
|
338
|
+
_write_settings
|
|
339
|
+
and settings._instance_exists
|
|
340
|
+
and f"{owner}/{name}" != settings.instance.slug
|
|
341
|
+
):
|
|
342
|
+
disconnect(mute=True)
|
|
343
|
+
|
|
344
|
+
try:
|
|
345
|
+
isettings = _connect_instance(
|
|
346
|
+
owner, name, db=_db, access_token=access_token
|
|
347
|
+
)
|
|
348
|
+
except InstanceNotFoundError as e:
|
|
349
|
+
if _raise_not_found_error:
|
|
350
|
+
raise e
|
|
351
|
+
else:
|
|
352
|
+
return "instance-not-found"
|
|
353
|
+
if isinstance(isettings, str):
|
|
354
|
+
return isettings
|
|
355
|
+
# at this point we have checked already that isettings is not a string
|
|
356
|
+
# _user is passed to lock cloud sqlite for this user in isettings._load_db()
|
|
357
|
+
# has no effect if _user is None or if not cloud sqlite instance
|
|
358
|
+
isettings._locker_user = _user
|
|
359
|
+
isettings._persist(write_to_disk=_write_settings)
|
|
360
|
+
if _test:
|
|
361
|
+
return None
|
|
362
|
+
silence_loggers()
|
|
363
|
+
check, msg = isettings._load_db()
|
|
364
|
+
if not check:
|
|
365
|
+
local_db = (
|
|
366
|
+
isettings._is_cloud_sqlite and isettings._sqlite_file_local.exists()
|
|
367
|
+
)
|
|
368
|
+
if local_db:
|
|
369
|
+
logger.warning(
|
|
370
|
+
"SQLite file does not exist in the cloud, but exists locally:"
|
|
371
|
+
f" {isettings._sqlite_file_local}\nTo push the file to the cloud,"
|
|
372
|
+
" call: lamin disconnect"
|
|
373
|
+
)
|
|
374
|
+
elif _raise_not_found_error:
|
|
375
|
+
raise SystemExit(msg)
|
|
376
|
+
else:
|
|
377
|
+
logger.warning(
|
|
378
|
+
f"instance exists with id {isettings._id.hex}, but database is not"
|
|
379
|
+
" loadable: re-initializing"
|
|
380
|
+
)
|
|
381
|
+
return "instance-corrupted-or-deleted"
|
|
382
|
+
# this is for testing purposes only
|
|
383
|
+
if _TEST_FAILED_LOAD:
|
|
384
|
+
raise RuntimeError("Technical testing error.")
|
|
385
|
+
|
|
386
|
+
load_from_isettings(isettings, user=_user, write_settings=_write_settings)
|
|
387
|
+
if _reload_lamindb:
|
|
388
|
+
importlib.reload(importlib.import_module("lamindb"))
|
|
389
|
+
reset_django_module_variables()
|
|
390
|
+
if isettings.slug != "none/none":
|
|
391
|
+
logger.important(f"connected lamindb: {isettings.slug}")
|
|
392
|
+
except Exception as e:
|
|
393
|
+
if isettings is not None:
|
|
394
|
+
if _write_settings:
|
|
395
|
+
isettings._get_settings_file().unlink(missing_ok=True) # type: ignore
|
|
396
|
+
settings._instance_settings = None
|
|
397
|
+
raise e
|
|
398
|
+
return None
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def get_owner_name_from_identifier(identifier: str):
|
|
402
|
+
if "/" in identifier:
|
|
403
|
+
if identifier.startswith("https://lamin.ai/"):
|
|
404
|
+
identifier = identifier.replace("https://lamin.ai/", "")
|
|
405
|
+
split = identifier.split("/")
|
|
406
|
+
if len(split) > 2:
|
|
407
|
+
raise ValueError(
|
|
408
|
+
"The instance identifier needs to be 'owner/name', the instance name"
|
|
409
|
+
" (owner is current user) or the URL: https://lamin.ai/owner/name."
|
|
410
|
+
)
|
|
411
|
+
owner, name = split
|
|
412
|
+
else:
|
|
413
|
+
owner = settings.user.handle
|
|
414
|
+
name = identifier
|
|
415
|
+
return owner, name
|