lamindb_setup 0.76.7__py2.py3-none-any.whl → 0.76.8__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.
Files changed (47) hide show
  1. lamindb_setup/__init__.py +1 -1
  2. lamindb_setup/_cache.py +34 -34
  3. lamindb_setup/_check.py +7 -7
  4. lamindb_setup/_check_setup.py +79 -79
  5. lamindb_setup/_close.py +35 -35
  6. lamindb_setup/_connect_instance.py +433 -433
  7. lamindb_setup/_delete.py +137 -137
  8. lamindb_setup/_django.py +41 -41
  9. lamindb_setup/_exportdb.py +68 -68
  10. lamindb_setup/_importdb.py +50 -50
  11. lamindb_setup/_init_instance.py +374 -374
  12. lamindb_setup/_migrate.py +239 -239
  13. lamindb_setup/_register_instance.py +36 -36
  14. lamindb_setup/_schema.py +27 -27
  15. lamindb_setup/_schema_metadata.py +411 -391
  16. lamindb_setup/_set_managed_storage.py +55 -55
  17. lamindb_setup/_setup_user.py +118 -118
  18. lamindb_setup/_silence_loggers.py +44 -44
  19. lamindb_setup/core/__init__.py +21 -21
  20. lamindb_setup/core/_aws_credentials.py +151 -151
  21. lamindb_setup/core/_aws_storage.py +48 -48
  22. lamindb_setup/core/_deprecated.py +55 -55
  23. lamindb_setup/core/_docs.py +14 -14
  24. lamindb_setup/core/_hub_client.py +164 -164
  25. lamindb_setup/core/_hub_core.py +473 -473
  26. lamindb_setup/core/_hub_crud.py +211 -211
  27. lamindb_setup/core/_hub_utils.py +109 -109
  28. lamindb_setup/core/_private_django_api.py +88 -88
  29. lamindb_setup/core/_settings.py +138 -138
  30. lamindb_setup/core/_settings_instance.py +461 -461
  31. lamindb_setup/core/_settings_load.py +100 -100
  32. lamindb_setup/core/_settings_save.py +81 -81
  33. lamindb_setup/core/_settings_storage.py +393 -393
  34. lamindb_setup/core/_settings_store.py +72 -72
  35. lamindb_setup/core/_settings_user.py +51 -51
  36. lamindb_setup/core/_setup_bionty_sources.py +101 -99
  37. lamindb_setup/core/cloud_sqlite_locker.py +232 -232
  38. lamindb_setup/core/django.py +113 -113
  39. lamindb_setup/core/exceptions.py +12 -12
  40. lamindb_setup/core/hashing.py +114 -114
  41. lamindb_setup/core/types.py +19 -19
  42. lamindb_setup/core/upath.py +779 -779
  43. {lamindb_setup-0.76.7.dist-info → lamindb_setup-0.76.8.dist-info}/METADATA +1 -1
  44. lamindb_setup-0.76.8.dist-info/RECORD +46 -0
  45. {lamindb_setup-0.76.7.dist-info → lamindb_setup-0.76.8.dist-info}/WHEEL +1 -1
  46. lamindb_setup-0.76.7.dist-info/RECORD +0 -46
  47. {lamindb_setup-0.76.7.dist-info → lamindb_setup-0.76.8.dist-info}/LICENSE +0 -0
@@ -1,374 +1,374 @@
1
- from __future__ import annotations
2
-
3
- import importlib
4
- import os
5
- import sys
6
- import uuid
7
- from typing import TYPE_CHECKING, Literal
8
- from uuid import UUID
9
-
10
- from django.core.exceptions import FieldError
11
- from django.db.utils import OperationalError, ProgrammingError
12
- from lamin_utils import logger
13
-
14
- from ._close import close as close_instance
15
- from ._silence_loggers import silence_loggers
16
- from .core import InstanceSettings
17
- from .core._settings import settings
18
- from .core._settings_instance import is_local_db_url
19
- from .core._settings_storage import StorageSettings, init_storage
20
- from .core.upath import UPath
21
-
22
- if TYPE_CHECKING:
23
- from pydantic import PostgresDsn
24
-
25
- from .core.types import UPathStr
26
-
27
-
28
- def get_schema_module_name(schema_name) -> str:
29
- import importlib.util
30
-
31
- name_attempts = [f"lnschema_{schema_name.replace('-', '_')}", schema_name]
32
- for name in name_attempts:
33
- module_spec = importlib.util.find_spec(name)
34
- if module_spec is not None:
35
- return name
36
- raise ImportError(
37
- f"Python package for '{schema_name}' is not installed.\nIf your package is on PyPI, run `pip install {schema_name}`"
38
- )
39
-
40
-
41
- def register_storage_in_instance(ssettings: StorageSettings):
42
- from lnschema_core.models import Storage
43
- from lnschema_core.users import current_user_id
44
-
45
- from .core.hashing import hash_and_encode_as_b62
46
-
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
51
- # how do we ensure that this function is only called passing
52
- # the managing instance?
53
- defaults = {
54
- "root": ssettings.root_as_str,
55
- "type": ssettings.type,
56
- "region": ssettings.region,
57
- "instance_uid": instance_uid,
58
- "created_by_id": current_user_id(),
59
- "run": None,
60
- }
61
- if ssettings._uid is not None:
62
- defaults["uid"] = ssettings._uid
63
- storage, _ = Storage.objects.update_or_create(
64
- root=ssettings.root_as_str,
65
- defaults=defaults,
66
- )
67
- return storage
68
-
69
-
70
- def register_user(usettings):
71
- from lnschema_core.models import User
72
-
73
- try:
74
- # need to have try except because of integer primary key migration
75
- user, created = User.objects.update_or_create(
76
- uid=usettings.uid,
77
- defaults={
78
- "handle": usettings.handle,
79
- "name": usettings.name,
80
- },
81
- )
82
- # for users with only read access, except via ProgrammingError
83
- # ProgrammingError: permission denied for table lnschema_core_user
84
- except (OperationalError, FieldError, ProgrammingError):
85
- pass
86
-
87
-
88
- def register_user_and_storage_in_instance(isettings: InstanceSettings, usettings):
89
- """Register user & storage in DB."""
90
- from django.db.utils import OperationalError
91
-
92
- try:
93
- register_user(usettings)
94
- register_storage_in_instance(isettings.storage)
95
- except OperationalError as error:
96
- logger.warning(f"instance seems not set up ({error})")
97
-
98
-
99
- def reload_schema_modules(isettings: InstanceSettings):
100
- schema_names = ["core"] + list(isettings.schema)
101
- schema_module_names = [get_schema_module_name(n) for n in schema_names]
102
-
103
- for schema_module_name in schema_module_names:
104
- if schema_module_name in sys.modules:
105
- schema_module = importlib.import_module(schema_module_name)
106
- importlib.reload(schema_module)
107
-
108
-
109
- def reload_lamindb_itself(isettings) -> bool:
110
- reloaded = False
111
- if "lamindb" in sys.modules:
112
- import lamindb
113
-
114
- importlib.reload(lamindb)
115
- reloaded = True
116
- if "bionty" in isettings.schema and "bionty" in sys.modules:
117
- schema_module = importlib.import_module("bionty")
118
- importlib.reload(schema_module)
119
- reloaded = True
120
- return reloaded
121
-
122
-
123
- def reload_lamindb(isettings: InstanceSettings):
124
- # only touch lamindb if we're operating from lamindb
125
- reload_schema_modules(isettings)
126
- log_message = settings.auto_connect
127
- if not reload_lamindb_itself(isettings):
128
- log_message = True
129
- if log_message:
130
- logger.important(f"connected lamindb: {isettings.slug}")
131
-
132
-
133
- ERROR_SQLITE_CACHE = """
134
- Your cached local SQLite file exists, while your cloud SQLite file ({}) doesn't.
135
- Either delete your cache ({}) or add it back to the cloud (if delete was accidental).
136
- """
137
-
138
-
139
- def process_connect_response(
140
- response: tuple | str, instance_identifier: str
141
- ) -> tuple[
142
- UUID,
143
- Literal[
144
- "instance-corrupted-or-deleted", "account-not-exists", "instance-not-found"
145
- ],
146
- ]:
147
- # for internal use when creating instances through CICD
148
- if isinstance(response, tuple) and response[0] == "instance-corrupted-or-deleted":
149
- hub_result = response[1]
150
- instance_state = response[0]
151
- instance_id = UUID(hub_result["id"])
152
- else:
153
- instance_id_str = os.getenv("LAMINDB_INSTANCE_ID_INIT")
154
- if instance_id_str is None:
155
- instance_id = uuid.uuid5(uuid.NAMESPACE_URL, instance_identifier)
156
- else:
157
- instance_id = UUID(instance_id_str)
158
- instance_state = response
159
- return instance_id, instance_state
160
-
161
-
162
- def validate_init_args(
163
- *,
164
- storage: UPathStr,
165
- name: str | None = None,
166
- db: PostgresDsn | None = None,
167
- schema: str | None = None,
168
- _test: bool = False,
169
- ) -> tuple[
170
- str,
171
- UUID | None,
172
- Literal[
173
- "connected",
174
- "instance-corrupted-or-deleted",
175
- "account-not-exists",
176
- "instance-not-found",
177
- ],
178
- str,
179
- ]:
180
- from ._connect_instance import connect
181
- from .core._hub_utils import (
182
- validate_schema_arg,
183
- )
184
-
185
- # should be called as the first thing
186
- name_str = infer_instance_name(storage=storage, name=name, db=db)
187
- # test whether instance exists by trying to load it
188
- instance_slug = f"{settings.user.handle}/{name_str}"
189
- response = connect(instance_slug, db=db, _raise_not_found_error=False, _test=_test)
190
- instance_state: Literal[
191
- "connected",
192
- "instance-corrupted-or-deleted",
193
- "account-not-exists",
194
- "instance-not-found",
195
- ] = "connected"
196
- instance_id = None
197
- if response is not None:
198
- instance_id, instance_state = process_connect_response(response, instance_slug)
199
- schema = validate_schema_arg(schema)
200
- return name_str, instance_id, instance_state, instance_slug
201
-
202
-
203
- MESSAGE_NO_MULTIPLE_INSTANCE = """
204
- Currently don't support subsequent connection to different databases in the same
205
- Python session.\n
206
- Try running on the CLI: lamin set auto-connect false
207
- """
208
-
209
-
210
- def init(
211
- *,
212
- storage: UPathStr,
213
- name: str | None = None,
214
- db: PostgresDsn | None = None,
215
- schema: str | None = None,
216
- _test: bool = False,
217
- ) -> None:
218
- """Create and load a LaminDB instance.
219
-
220
- Args:
221
- storage: Either ``"create-s3"``, local or
222
- remote folder (``"s3://..."`` or ``"gs://..."``).
223
- name: Instance name.
224
- db: Database connection url, do not pass for SQLite.
225
- schema: Comma-separated string of schema modules. None if not set.
226
- """
227
- isettings = None
228
- ssettings = None
229
- try:
230
- silence_loggers()
231
- from ._check_setup import _check_instance_setup
232
-
233
- if _check_instance_setup() and not _test:
234
- raise RuntimeError(MESSAGE_NO_MULTIPLE_INSTANCE)
235
- else:
236
- close_instance(mute=True)
237
- from .core._hub_core import init_instance as init_instance_hub
238
-
239
- name_str, instance_id, instance_state, _ = validate_init_args(
240
- storage=storage,
241
- name=name,
242
- db=db,
243
- schema=schema,
244
- _test=_test,
245
- )
246
- if instance_state == "connected":
247
- settings.auto_connect = True # we can also debate this switch here
248
- return None
249
- # the conditions here match `isettings.is_remote`, but I currently don't
250
- # see a way of making this more elegant; should become possible if we
251
- # remove the instance.storage_id FK on the hub
252
- prevent_register_hub = is_local_db_url(db) if db is not None else False
253
- ssettings, _ = init_storage(
254
- storage,
255
- instance_id=instance_id,
256
- init_instance=True,
257
- prevent_register_hub=prevent_register_hub,
258
- )
259
- isettings = InstanceSettings(
260
- id=instance_id, # type: ignore
261
- owner=settings.user.handle,
262
- name=name_str,
263
- storage=ssettings,
264
- db=db,
265
- schema=schema,
266
- uid=ssettings.uid,
267
- )
268
- register_on_hub = (
269
- isettings.is_remote and instance_state != "instance-corrupted-or-deleted"
270
- )
271
- if register_on_hub:
272
- init_instance_hub(isettings)
273
- validate_sqlite_state(isettings)
274
- isettings._persist()
275
- if _test:
276
- return None
277
- isettings._init_db()
278
- load_from_isettings(isettings, init=True)
279
- if isettings._is_cloud_sqlite:
280
- isettings._cloud_sqlite_locker.lock()
281
- logger.warning(
282
- "locked instance (to unlock and push changes to the cloud SQLite file,"
283
- " call: lamin close)"
284
- )
285
- if register_on_hub and isettings.dialect != "sqlite":
286
- from ._schema_metadata import update_schema_in_hub
287
-
288
- update_schema_in_hub()
289
- settings.auto_connect = True
290
- except Exception as e:
291
- from ._delete import delete_by_isettings
292
- from .core._hub_core import delete_instance_record, delete_storage_record
293
-
294
- if isettings is not None:
295
- delete_by_isettings(isettings)
296
- if settings.user.handle != "anonymous" and isettings.is_on_hub:
297
- delete_instance_record(isettings._id)
298
- isettings._get_settings_file().unlink(missing_ok=True) # type: ignore
299
- if (
300
- ssettings is not None
301
- and settings.user.handle != "anonymous"
302
- and ssettings.is_on_hub
303
- ):
304
- delete_storage_record(ssettings._uuid) # type: ignore
305
- raise e
306
- return None
307
-
308
-
309
- def load_from_isettings(
310
- isettings: InstanceSettings,
311
- *,
312
- init: bool = False,
313
- ) -> None:
314
- from .core._setup_bionty_sources import load_bionty_sources, write_bionty_sources
315
-
316
- if init:
317
- # during init both user and storage need to be registered
318
- register_user_and_storage_in_instance(isettings, settings.user)
319
- write_bionty_sources(isettings)
320
- isettings._update_cloud_sqlite_file(unlock_cloud_sqlite=False)
321
- else:
322
- # when loading, django is already set up
323
- #
324
- # only register user if the instance is connected
325
- # for the first time in an environment
326
- # this is our best proxy for that the user might not
327
- # yet be registered
328
- if not isettings._get_settings_file().exists():
329
- register_user(settings.user)
330
- load_bionty_sources(isettings)
331
- isettings._persist()
332
- reload_lamindb(isettings)
333
-
334
-
335
- def validate_sqlite_state(isettings: InstanceSettings) -> None:
336
- if isettings._is_cloud_sqlite:
337
- if (
338
- # it's important to first evaluate the existence check
339
- # for the local sqlite file because it doesn't need a network
340
- # request
341
- isettings._sqlite_file_local.exists()
342
- and not isettings._sqlite_file.exists()
343
- ):
344
- raise RuntimeError(
345
- ERROR_SQLITE_CACHE.format(
346
- isettings._sqlite_file, isettings._sqlite_file_local
347
- )
348
- )
349
-
350
-
351
- def infer_instance_name(
352
- *,
353
- storage: UPathStr,
354
- name: str | None = None,
355
- db: PostgresDsn | None = None,
356
- ) -> str:
357
- if name is not None:
358
- if "/" in name:
359
- raise ValueError("Invalid instance name: '/' delimiter not allowed.")
360
- return name
361
- if db is not None:
362
- logger.warning("using the sql database name for the instance name")
363
- # this isn't a great way to access the db name
364
- # could use LaminDsn instead
365
- return str(db).split("/")[-1]
366
- if storage == "create-s3":
367
- raise ValueError("pass name to init if storage = 'create-s3'")
368
- storage_path = UPath(storage)
369
- if storage_path.name != "":
370
- name = storage_path.name
371
- else:
372
- # dedicated treatment of bucket names
373
- name = storage_path._url.netloc
374
- return name.lower()
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ import os
5
+ import sys
6
+ import uuid
7
+ from typing import TYPE_CHECKING, Literal
8
+ from uuid import UUID
9
+
10
+ from django.core.exceptions import FieldError
11
+ from django.db.utils import OperationalError, ProgrammingError
12
+ from lamin_utils import logger
13
+
14
+ from ._close import close as close_instance
15
+ from ._silence_loggers import silence_loggers
16
+ from .core import InstanceSettings
17
+ from .core._settings import settings
18
+ from .core._settings_instance import is_local_db_url
19
+ from .core._settings_storage import StorageSettings, init_storage
20
+ from .core.upath import UPath
21
+
22
+ if TYPE_CHECKING:
23
+ from pydantic import PostgresDsn
24
+
25
+ from .core.types import UPathStr
26
+
27
+
28
+ def get_schema_module_name(schema_name) -> str:
29
+ import importlib.util
30
+
31
+ name_attempts = [f"lnschema_{schema_name.replace('-', '_')}", schema_name]
32
+ for name in name_attempts:
33
+ module_spec = importlib.util.find_spec(name)
34
+ if module_spec is not None:
35
+ return name
36
+ raise ImportError(
37
+ f"Python package for '{schema_name}' is not installed.\nIf your package is on PyPI, run `pip install {schema_name}`"
38
+ )
39
+
40
+
41
+ def register_storage_in_instance(ssettings: StorageSettings):
42
+ from lnschema_core.models import Storage
43
+ from lnschema_core.users import current_user_id
44
+
45
+ from .core.hashing import hash_and_encode_as_b62
46
+
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
51
+ # how do we ensure that this function is only called passing
52
+ # the managing instance?
53
+ defaults = {
54
+ "root": ssettings.root_as_str,
55
+ "type": ssettings.type,
56
+ "region": ssettings.region,
57
+ "instance_uid": instance_uid,
58
+ "created_by_id": current_user_id(),
59
+ "run": None,
60
+ }
61
+ if ssettings._uid is not None:
62
+ defaults["uid"] = ssettings._uid
63
+ storage, _ = Storage.objects.update_or_create(
64
+ root=ssettings.root_as_str,
65
+ defaults=defaults,
66
+ )
67
+ return storage
68
+
69
+
70
+ def register_user(usettings):
71
+ from lnschema_core.models import User
72
+
73
+ try:
74
+ # need to have try except because of integer primary key migration
75
+ user, created = User.objects.update_or_create(
76
+ uid=usettings.uid,
77
+ defaults={
78
+ "handle": usettings.handle,
79
+ "name": usettings.name,
80
+ },
81
+ )
82
+ # for users with only read access, except via ProgrammingError
83
+ # ProgrammingError: permission denied for table lnschema_core_user
84
+ except (OperationalError, FieldError, ProgrammingError):
85
+ pass
86
+
87
+
88
+ def register_user_and_storage_in_instance(isettings: InstanceSettings, usettings):
89
+ """Register user & storage in DB."""
90
+ from django.db.utils import OperationalError
91
+
92
+ try:
93
+ register_user(usettings)
94
+ register_storage_in_instance(isettings.storage)
95
+ except OperationalError as error:
96
+ logger.warning(f"instance seems not set up ({error})")
97
+
98
+
99
+ def reload_schema_modules(isettings: InstanceSettings):
100
+ schema_names = ["core"] + list(isettings.schema)
101
+ schema_module_names = [get_schema_module_name(n) for n in schema_names]
102
+
103
+ for schema_module_name in schema_module_names:
104
+ if schema_module_name in sys.modules:
105
+ schema_module = importlib.import_module(schema_module_name)
106
+ importlib.reload(schema_module)
107
+
108
+
109
+ def reload_lamindb_itself(isettings) -> bool:
110
+ reloaded = False
111
+ if "lamindb" in sys.modules:
112
+ import lamindb
113
+
114
+ importlib.reload(lamindb)
115
+ reloaded = True
116
+ if "bionty" in isettings.schema and "bionty" in sys.modules:
117
+ schema_module = importlib.import_module("bionty")
118
+ importlib.reload(schema_module)
119
+ reloaded = True
120
+ return reloaded
121
+
122
+
123
+ def reload_lamindb(isettings: InstanceSettings):
124
+ # only touch lamindb if we're operating from lamindb
125
+ reload_schema_modules(isettings)
126
+ log_message = settings.auto_connect
127
+ if not reload_lamindb_itself(isettings):
128
+ log_message = True
129
+ if log_message:
130
+ logger.important(f"connected lamindb: {isettings.slug}")
131
+
132
+
133
+ ERROR_SQLITE_CACHE = """
134
+ Your cached local SQLite file exists, while your cloud SQLite file ({}) doesn't.
135
+ Either delete your cache ({}) or add it back to the cloud (if delete was accidental).
136
+ """
137
+
138
+
139
+ def process_connect_response(
140
+ response: tuple | str, instance_identifier: str
141
+ ) -> tuple[
142
+ UUID,
143
+ Literal[
144
+ "instance-corrupted-or-deleted", "account-not-exists", "instance-not-found"
145
+ ],
146
+ ]:
147
+ # for internal use when creating instances through CICD
148
+ if isinstance(response, tuple) and response[0] == "instance-corrupted-or-deleted":
149
+ hub_result = response[1]
150
+ instance_state = response[0]
151
+ instance_id = UUID(hub_result["id"])
152
+ else:
153
+ instance_id_str = os.getenv("LAMINDB_INSTANCE_ID_INIT")
154
+ if instance_id_str is None:
155
+ instance_id = uuid.uuid5(uuid.NAMESPACE_URL, instance_identifier)
156
+ else:
157
+ instance_id = UUID(instance_id_str)
158
+ instance_state = response
159
+ return instance_id, instance_state
160
+
161
+
162
+ def validate_init_args(
163
+ *,
164
+ storage: UPathStr,
165
+ name: str | None = None,
166
+ db: PostgresDsn | None = None,
167
+ schema: str | None = None,
168
+ _test: bool = False,
169
+ ) -> tuple[
170
+ str,
171
+ UUID | None,
172
+ Literal[
173
+ "connected",
174
+ "instance-corrupted-or-deleted",
175
+ "account-not-exists",
176
+ "instance-not-found",
177
+ ],
178
+ str,
179
+ ]:
180
+ from ._connect_instance import connect
181
+ from .core._hub_utils import (
182
+ validate_schema_arg,
183
+ )
184
+
185
+ # should be called as the first thing
186
+ name_str = infer_instance_name(storage=storage, name=name, db=db)
187
+ # test whether instance exists by trying to load it
188
+ instance_slug = f"{settings.user.handle}/{name_str}"
189
+ response = connect(instance_slug, db=db, _raise_not_found_error=False, _test=_test)
190
+ instance_state: Literal[
191
+ "connected",
192
+ "instance-corrupted-or-deleted",
193
+ "account-not-exists",
194
+ "instance-not-found",
195
+ ] = "connected"
196
+ instance_id = None
197
+ if response is not None:
198
+ instance_id, instance_state = process_connect_response(response, instance_slug)
199
+ schema = validate_schema_arg(schema)
200
+ return name_str, instance_id, instance_state, instance_slug
201
+
202
+
203
+ MESSAGE_NO_MULTIPLE_INSTANCE = """
204
+ Currently don't support subsequent connection to different databases in the same
205
+ Python session.\n
206
+ Try running on the CLI: lamin set auto-connect false
207
+ """
208
+
209
+
210
+ def init(
211
+ *,
212
+ storage: UPathStr,
213
+ name: str | None = None,
214
+ db: PostgresDsn | None = None,
215
+ schema: str | None = None,
216
+ _test: bool = False,
217
+ ) -> None:
218
+ """Create and load a LaminDB instance.
219
+
220
+ Args:
221
+ storage: Either ``"create-s3"``, local or
222
+ remote folder (``"s3://..."`` or ``"gs://..."``).
223
+ name: Instance name.
224
+ db: Database connection url, do not pass for SQLite.
225
+ schema: Comma-separated string of schema modules. None if not set.
226
+ """
227
+ isettings = None
228
+ ssettings = None
229
+ try:
230
+ silence_loggers()
231
+ from ._check_setup import _check_instance_setup
232
+
233
+ if _check_instance_setup() and not _test:
234
+ raise RuntimeError(MESSAGE_NO_MULTIPLE_INSTANCE)
235
+ else:
236
+ close_instance(mute=True)
237
+ from .core._hub_core import init_instance as init_instance_hub
238
+
239
+ name_str, instance_id, instance_state, _ = validate_init_args(
240
+ storage=storage,
241
+ name=name,
242
+ db=db,
243
+ schema=schema,
244
+ _test=_test,
245
+ )
246
+ if instance_state == "connected":
247
+ settings.auto_connect = True # we can also debate this switch here
248
+ return None
249
+ # the conditions here match `isettings.is_remote`, but I currently don't
250
+ # see a way of making this more elegant; should become possible if we
251
+ # remove the instance.storage_id FK on the hub
252
+ prevent_register_hub = is_local_db_url(db) if db is not None else False
253
+ ssettings, _ = init_storage(
254
+ storage,
255
+ instance_id=instance_id,
256
+ init_instance=True,
257
+ prevent_register_hub=prevent_register_hub,
258
+ )
259
+ isettings = InstanceSettings(
260
+ id=instance_id, # type: ignore
261
+ owner=settings.user.handle,
262
+ name=name_str,
263
+ storage=ssettings,
264
+ db=db,
265
+ schema=schema,
266
+ uid=ssettings.uid,
267
+ )
268
+ register_on_hub = (
269
+ isettings.is_remote and instance_state != "instance-corrupted-or-deleted"
270
+ )
271
+ if register_on_hub:
272
+ init_instance_hub(isettings)
273
+ validate_sqlite_state(isettings)
274
+ isettings._persist()
275
+ if _test:
276
+ return None
277
+ isettings._init_db()
278
+ load_from_isettings(isettings, init=True)
279
+ if isettings._is_cloud_sqlite:
280
+ isettings._cloud_sqlite_locker.lock()
281
+ logger.warning(
282
+ "locked instance (to unlock and push changes to the cloud SQLite file,"
283
+ " call: lamin close)"
284
+ )
285
+ if register_on_hub and isettings.dialect != "sqlite":
286
+ from ._schema_metadata import update_schema_in_hub
287
+
288
+ update_schema_in_hub()
289
+ settings.auto_connect = True
290
+ except Exception as e:
291
+ from ._delete import delete_by_isettings
292
+ from .core._hub_core import delete_instance_record, delete_storage_record
293
+
294
+ if isettings is not None:
295
+ delete_by_isettings(isettings)
296
+ if settings.user.handle != "anonymous" and isettings.is_on_hub:
297
+ delete_instance_record(isettings._id)
298
+ isettings._get_settings_file().unlink(missing_ok=True) # type: ignore
299
+ if (
300
+ ssettings is not None
301
+ and settings.user.handle != "anonymous"
302
+ and ssettings.is_on_hub
303
+ ):
304
+ delete_storage_record(ssettings._uuid) # type: ignore
305
+ raise e
306
+ return None
307
+
308
+
309
+ def load_from_isettings(
310
+ isettings: InstanceSettings,
311
+ *,
312
+ init: bool = False,
313
+ ) -> None:
314
+ from .core._setup_bionty_sources import load_bionty_sources, write_bionty_sources
315
+
316
+ if init:
317
+ # during init both user and storage need to be registered
318
+ register_user_and_storage_in_instance(isettings, settings.user)
319
+ write_bionty_sources(isettings)
320
+ isettings._update_cloud_sqlite_file(unlock_cloud_sqlite=False)
321
+ else:
322
+ # when loading, django is already set up
323
+ #
324
+ # only register user if the instance is connected
325
+ # for the first time in an environment
326
+ # this is our best proxy for that the user might not
327
+ # yet be registered
328
+ if not isettings._get_settings_file().exists():
329
+ register_user(settings.user)
330
+ load_bionty_sources(isettings)
331
+ isettings._persist()
332
+ reload_lamindb(isettings)
333
+
334
+
335
+ def validate_sqlite_state(isettings: InstanceSettings) -> None:
336
+ if isettings._is_cloud_sqlite:
337
+ if (
338
+ # it's important to first evaluate the existence check
339
+ # for the local sqlite file because it doesn't need a network
340
+ # request
341
+ isettings._sqlite_file_local.exists()
342
+ and not isettings._sqlite_file.exists()
343
+ ):
344
+ raise RuntimeError(
345
+ ERROR_SQLITE_CACHE.format(
346
+ isettings._sqlite_file, isettings._sqlite_file_local
347
+ )
348
+ )
349
+
350
+
351
+ def infer_instance_name(
352
+ *,
353
+ storage: UPathStr,
354
+ name: str | None = None,
355
+ db: PostgresDsn | None = None,
356
+ ) -> str:
357
+ if name is not None:
358
+ if "/" in name:
359
+ raise ValueError("Invalid instance name: '/' delimiter not allowed.")
360
+ return name
361
+ if db is not None:
362
+ logger.warning("using the sql database name for the instance name")
363
+ # this isn't a great way to access the db name
364
+ # could use LaminDsn instead
365
+ return str(db).split("/")[-1]
366
+ if storage == "create-s3":
367
+ raise ValueError("pass name to init if storage = 'create-s3'")
368
+ storage_path = UPath(storage)
369
+ if storage_path.name != "":
370
+ name = storage_path.name
371
+ else:
372
+ # dedicated treatment of bucket names
373
+ name = storage_path._url.netloc
374
+ return name.lower()