lamindb_setup 0.70.0__py2.py3-none-any.whl → 0.71.1__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 (45) hide show
  1. lamindb_setup/__init__.py +15 -15
  2. lamindb_setup/_cache.py +4 -1
  3. lamindb_setup/_check.py +3 -0
  4. lamindb_setup/_check_setup.py +13 -7
  5. lamindb_setup/_close.py +2 -0
  6. lamindb_setup/_connect_instance.py +47 -26
  7. lamindb_setup/_delete.py +72 -40
  8. lamindb_setup/_django.py +4 -1
  9. lamindb_setup/_exportdb.py +4 -2
  10. lamindb_setup/_importdb.py +5 -1
  11. lamindb_setup/_init_instance.py +61 -45
  12. lamindb_setup/_migrate.py +16 -13
  13. lamindb_setup/_register_instance.py +10 -3
  14. lamindb_setup/_schema.py +6 -3
  15. lamindb_setup/_set_managed_storage.py +37 -0
  16. lamindb_setup/_setup_user.py +7 -7
  17. lamindb_setup/_silence_loggers.py +4 -2
  18. lamindb_setup/core/__init__.py +4 -3
  19. lamindb_setup/core/_aws_storage.py +3 -0
  20. lamindb_setup/core/_deprecated.py +2 -7
  21. lamindb_setup/core/_docs.py +2 -0
  22. lamindb_setup/core/_hub_client.py +12 -10
  23. lamindb_setup/core/_hub_core.py +203 -88
  24. lamindb_setup/core/_hub_crud.py +21 -12
  25. lamindb_setup/core/_hub_utils.py +11 -8
  26. lamindb_setup/core/_settings.py +23 -26
  27. lamindb_setup/core/_settings_instance.py +149 -81
  28. lamindb_setup/core/_settings_load.py +13 -7
  29. lamindb_setup/core/_settings_save.py +13 -8
  30. lamindb_setup/core/_settings_storage.py +76 -42
  31. lamindb_setup/core/_settings_store.py +4 -2
  32. lamindb_setup/core/_settings_user.py +10 -6
  33. lamindb_setup/core/_setup_bionty_sources.py +9 -2
  34. lamindb_setup/core/cloud_sqlite_locker.py +13 -10
  35. lamindb_setup/core/django.py +3 -1
  36. lamindb_setup/core/exceptions.py +4 -2
  37. lamindb_setup/core/hashing.py +15 -5
  38. lamindb_setup/core/types.py +5 -2
  39. lamindb_setup/core/upath.py +191 -88
  40. {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.1.dist-info}/METADATA +6 -4
  41. lamindb_setup-0.71.1.dist-info/RECORD +43 -0
  42. lamindb_setup/_add_remote_storage.py +0 -50
  43. lamindb_setup-0.70.0.dist-info/RECORD +0 -43
  44. {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.1.dist-info}/LICENSE +0 -0
  45. {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.1.dist-info}/WHEEL +0 -0
@@ -1,35 +1,41 @@
1
+ from __future__ import annotations
2
+
3
+ import json
1
4
  import os
2
- from typing import Optional, Tuple, Union, Dict
3
5
  import uuid
4
- from postgrest.exceptions import APIError
5
- from uuid import UUID
6
- from lamin_utils import logger
7
- from supabase import Client
8
- import lamindb_setup
9
- import json
10
6
  from importlib import metadata
11
- from ._settings_instance import InstanceSettings
12
- from ._settings_storage import StorageSettings, base62
7
+ from typing import TYPE_CHECKING
8
+ from uuid import UUID
13
9
 
10
+ from lamin_utils import logger
11
+ from postgrest.exceptions import APIError
14
12
 
15
13
  from ._hub_client import (
16
- connect_hub,
17
- call_with_fallback_auth,
18
14
  call_with_fallback,
15
+ call_with_fallback_auth,
16
+ connect_hub,
19
17
  )
20
18
  from ._hub_crud import (
21
- select_instance_by_owner_name,
22
- select_instance_by_id_with_storage,
19
+ _delete_instance_record,
23
20
  select_account_by_handle,
24
21
  select_db_user_by_instance,
22
+ select_instance_by_id_with_storage,
25
23
  select_instance_by_name,
24
+ select_instance_by_owner_name,
26
25
  select_storage,
27
- _delete_instance_record,
28
26
  )
27
+ from ._hub_crud import update_instance as _update_instance_record
29
28
  from ._hub_utils import (
30
29
  LaminDsn,
31
30
  LaminDsnModel,
32
31
  )
32
+ from ._settings import settings
33
+ from ._settings_storage import StorageSettings, base62
34
+
35
+ if TYPE_CHECKING:
36
+ from supabase import Client # type: ignore
37
+
38
+ from ._settings_instance import InstanceSettings
33
39
 
34
40
 
35
41
  def delete_storage_record(
@@ -44,88 +50,192 @@ def delete_storage_record(
44
50
  def _delete_storage_record(storage_uuid: UUID, client: Client) -> None:
45
51
  if storage_uuid is None:
46
52
  return None
47
- logger.important(f"deleting storage {storage_uuid.hex}")
48
- client.table("storage").delete().eq("id", storage_uuid.hex).execute()
53
+ response = client.table("storage").delete().eq("id", storage_uuid.hex).execute()
54
+ if response.data:
55
+ logger.important(f"deleted storage record on hub {storage_uuid.hex}")
56
+ else:
57
+ raise PermissionError(
58
+ f"Deleting of storage with {storage_uuid.hex} was not successful. Probably, you"
59
+ " don't have sufficient permissions."
60
+ )
49
61
 
50
62
 
51
- def update_instance_record(instance_uuid: UUID, fields: Dict) -> None:
52
- from ._hub_crud import update_instance
63
+ def update_instance_record(instance_uuid: UUID, fields: dict) -> None:
64
+ return call_with_fallback_auth(
65
+ _update_instance_record, instance_id=instance_uuid.hex, instance_fields=fields
66
+ )
67
+
53
68
 
69
+ def get_storage_records_for_instance(
70
+ instance_id: UUID,
71
+ ) -> list[dict[str, str | int]]:
54
72
  return call_with_fallback_auth(
55
- update_instance, instance_id=instance_uuid.hex, instance_fields=fields
73
+ _get_storage_records_for_instance,
74
+ instance_id=instance_id,
56
75
  )
57
76
 
58
77
 
78
+ def _get_storage_records_for_instance(
79
+ instance_id: UUID, client: Client
80
+ ) -> list[dict[str, str | int]]:
81
+ response = (
82
+ client.table("storage").select("*").eq("instance_id", instance_id.hex).execute()
83
+ )
84
+ return response.data
85
+
86
+
87
+ def _select_storage(
88
+ ssettings: StorageSettings, update_uid: bool, client: Client
89
+ ) -> bool:
90
+ root = ssettings.root_as_str
91
+ response = client.table("storage").select("*").eq("root", root).execute()
92
+ if not response.data:
93
+ return False
94
+ else:
95
+ existing_storage = response.data[0]
96
+ if ssettings._instance_id is not None:
97
+ # consider storage settings that are meant to be managed by an instance
98
+ if UUID(existing_storage["instance_id"]) != ssettings._instance_id:
99
+ # everything is alright if the instance_id matches
100
+ # we're probably just switching storage locations
101
+ # below can be turned into a warning and then delegate the error
102
+ # to a unique constraint violation below
103
+ raise ValueError(
104
+ f"Storage root {root} is already managed by instance {existing_storage['instance_id']}."
105
+ )
106
+ else:
107
+ # if the request is agnostic of the instance, that's alright,
108
+ # we'll update the instance_id with what's stored in the hub
109
+ ssettings._instance_id = UUID(existing_storage["instance_id"])
110
+ ssettings._uuid_ = UUID(existing_storage["id"])
111
+ if update_uid:
112
+ ssettings._uid = existing_storage["lnid"]
113
+ else:
114
+ assert ssettings._uid == existing_storage["lnid"]
115
+ return True
116
+
117
+
59
118
  def init_storage(
60
119
  ssettings: StorageSettings,
61
- ) -> UUID:
62
- return call_with_fallback_auth(
63
- _init_storage,
64
- ssettings=ssettings,
65
- )
120
+ ) -> None:
121
+ if settings.user.handle != "anonymous":
122
+ return call_with_fallback_auth(
123
+ _init_storage,
124
+ ssettings=ssettings,
125
+ )
126
+ else:
127
+ storage_exists = call_with_fallback(
128
+ _select_storage, ssettings=ssettings, update_uid=True
129
+ )
130
+ if storage_exists:
131
+ return None
132
+ else:
133
+ raise ValueError("Log in to create a storage location on the hub.")
66
134
 
67
135
 
68
- def _init_storage(ssettings: StorageSettings, client: Client) -> UUID:
136
+ def _init_storage(ssettings: StorageSettings, client: Client) -> None:
69
137
  from lamindb_setup import settings
70
138
 
71
139
  # storage roots are always stored without the trailing slash in the SQL
72
140
  # database
73
141
  root = ssettings.root_as_str
74
- # in the future, we could also encode a prefix to model local storage
75
- # locations
76
- # f"{prefix}{root}"
77
- id = uuid.uuid5(uuid.NAMESPACE_URL, root)
78
- fields = dict(
79
- id=id.hex,
80
- lnid=ssettings.uid,
81
- created_by=settings.user._uuid.hex, # type: ignore
82
- root=root,
83
- region=ssettings.region,
84
- type=ssettings.type,
85
- aws_account_id=ssettings._aws_account_id,
86
- description=ssettings._description,
87
- )
142
+ if _select_storage(ssettings, update_uid=True, client=client):
143
+ return None
144
+ if ssettings.type_is_cloud:
145
+ id = uuid.uuid5(uuid.NAMESPACE_URL, root)
146
+ else:
147
+ id = uuid.uuid4()
148
+ if ssettings._instance_id is None:
149
+ logger.warning(
150
+ f"will manage storage location {ssettings.root_as_str} with instance {settings.instance.slug}"
151
+ )
152
+ ssettings._instance_id = settings.instance._id
153
+ fields = {
154
+ "id": id.hex,
155
+ "lnid": ssettings.uid,
156
+ "created_by": settings.user._uuid.hex, # type: ignore
157
+ "root": root,
158
+ "region": ssettings.region,
159
+ "type": ssettings.type,
160
+ "instance_id": ssettings._instance_id.hex,
161
+ # the empty string is important as we want the user flow to be through LaminHub
162
+ # if this errors with unique constraint error, the user has to update
163
+ # the description in LaminHub
164
+ "description": "",
165
+ }
166
+ # TODO: add error message for violated unique constraint
167
+ # on root & description
88
168
  client.table("storage").upsert(fields).execute()
89
- return id
169
+ ssettings._uuid_ = id
170
+ return None
171
+
172
+
173
+ def delete_instance(identifier: UUID | str, require_empty: bool = True) -> str | None:
174
+ return call_with_fallback_auth(
175
+ _delete_instance, identifier=identifier, require_empty=require_empty
176
+ )
90
177
 
91
178
 
92
- def delete_instance(identifier: Union[UUID, str], require_empty: bool = True) -> None:
179
+ def _delete_instance(
180
+ identifier: UUID | str, require_empty: bool, client: Client
181
+ ) -> str | None:
93
182
  """Fully delete an instance in the hub.
94
183
 
95
184
  This function deletes the relevant instance and storage records in the hub,
96
185
  conditional on the emptiness of the storage location.
97
186
  """
98
- from .upath import check_storage_is_empty, create_path
99
187
  from ._settings_storage import mark_storage_root
188
+ from .upath import check_storage_is_empty, create_path
100
189
 
101
- if isinstance(identifier, UUID):
102
- instance_with_storage = call_with_fallback_auth(
103
- select_instance_by_id_with_storage,
104
- instance_id=identifier,
190
+ # the "/" check is for backward compatibility with the old identifier format
191
+ if isinstance(identifier, UUID) or "/" not in identifier:
192
+ if isinstance(identifier, UUID):
193
+ instance_id_str = identifier.hex
194
+ else:
195
+ instance_id_str = identifier
196
+ instance_with_storage = select_instance_by_id_with_storage(
197
+ instance_id=instance_id_str, client=client
105
198
  )
106
199
  else:
107
200
  owner, name = identifier.split("/")
108
- instance_with_storage = call_with_fallback_auth(
109
- select_instance_by_owner_name,
110
- owner=owner,
111
- name=name,
201
+ instance_with_storage = select_instance_by_owner_name(
202
+ owner=owner, name=name, client=client
112
203
  )
113
204
 
114
205
  if instance_with_storage is None:
115
- logger.warning("instance not found")
116
- return None
206
+ logger.important("not deleting instance from hub as instance not found there")
207
+ return "instance-not-found"
117
208
 
209
+ storage_records = _get_storage_records_for_instance(
210
+ UUID(instance_with_storage["id"]),
211
+ client,
212
+ )
118
213
  if require_empty:
119
- root_string = instance_with_storage["storage"]["root"]
120
- # gate storage and instance deletion on empty storage location for
121
- root_path = create_path(root_string)
122
- mark_storage_root(root_path) # address permission error
123
- account_for_sqlite_file = instance_with_storage["db_scheme"] is None
124
- check_storage_is_empty(
125
- root_path, account_for_sqlite_file=account_for_sqlite_file
126
- )
127
- delete_instance_record(UUID(instance_with_storage["id"]))
128
- delete_storage_record(UUID(instance_with_storage["storage_id"]))
214
+ for storage_record in storage_records:
215
+ account_for_sqlite_file = (
216
+ instance_with_storage["db_scheme"] is None
217
+ and instance_with_storage["storage"]["root"] == storage_record["root"]
218
+ )
219
+ root_string = storage_record["root"]
220
+ # gate storage and instance deletion on empty storage location for
221
+ if client.auth.get_session() is not None:
222
+ access_token = client.auth.get_session().access_token
223
+ else:
224
+ access_token = None
225
+ root_path = create_path(root_string, access_token)
226
+ mark_storage_root(
227
+ root_path,
228
+ storage_record["lnid"], # type: ignore
229
+ ) # address permission error
230
+ check_storage_is_empty(
231
+ root_path, account_for_sqlite_file=account_for_sqlite_file
232
+ )
233
+ _update_instance_record(instance_with_storage["id"], {"storage_id": None}, client)
234
+ # first delete the storage records because we will turn instance_id on
235
+ # storage into a FK soon
236
+ for storage_record in storage_records:
237
+ _delete_storage_record(UUID(storage_record["id"]), client) # type: ignore
238
+ _delete_instance_record(UUID(instance_with_storage["id"]), client)
129
239
  return None
130
240
 
131
241
 
@@ -149,25 +259,25 @@ def _init_instance(isettings: InstanceSettings, client: Client) -> None:
149
259
  lamindb_version = metadata.version("lamindb")
150
260
  except metadata.PackageNotFoundError:
151
261
  lamindb_version = None
152
- fields = dict(
153
- id=isettings.id.hex,
154
- account_id=settings.user._uuid.hex, # type: ignore
155
- name=isettings.name,
156
- storage_id=isettings.storage._uuid.hex, # type: ignore
157
- schema_str=isettings._schema_str,
158
- lamindb_version=lamindb_version,
159
- public=False,
160
- )
262
+ fields = {
263
+ "id": isettings._id.hex,
264
+ "account_id": settings.user._uuid.hex, # type: ignore
265
+ "name": isettings.name,
266
+ "storage_id": isettings.storage._uuid.hex, # type: ignore
267
+ "lnid": isettings.uid,
268
+ "schema_str": isettings._schema_str,
269
+ "lamindb_version": lamindb_version,
270
+ "public": False,
271
+ }
161
272
  if isettings.dialect != "sqlite":
162
273
  db_dsn = LaminDsnModel(db=isettings.db)
163
- fields.update(
164
- {
165
- "db_scheme": db_dsn.db.scheme,
166
- "db_host": db_dsn.db.host,
167
- "db_port": db_dsn.db.port,
168
- "db_database": db_dsn.db.database,
169
- }
170
- )
274
+ db_fields = {
275
+ "db_scheme": db_dsn.db.scheme,
276
+ "db_host": db_dsn.db.host,
277
+ "db_port": db_dsn.db.port,
278
+ "db_database": db_dsn.db.database,
279
+ }
280
+ fields.update(db_fields)
171
281
  # I'd like the following to be an upsert, but this seems to violate RLS
172
282
  # Similarly, if we don't specify `returning="minimal"`, we'll violate RLS
173
283
  # we could make this idempotent by catching an error, but this seems dangerous
@@ -177,6 +287,9 @@ def _init_instance(isettings: InstanceSettings, client: Client) -> None:
177
287
  except APIError as e:
178
288
  logger.warning("instance likely already exists")
179
289
  raise e
290
+ client.table("storage").update(
291
+ {"instance_id": isettings._id.hex, "is_default": True}
292
+ ).eq("id", isettings.storage._uuid.hex).execute() # type: ignore
180
293
  logger.save(f"browse to: https://lamin.ai/{isettings.owner}/{isettings.name}")
181
294
 
182
295
 
@@ -184,7 +297,7 @@ def connect_instance(
184
297
  *,
185
298
  owner: str, # account_handle
186
299
  name: str, # instance_name
187
- ) -> Union[Tuple[dict, dict], str]:
300
+ ) -> tuple[dict, dict] | str:
188
301
  from ._settings import settings
189
302
 
190
303
  if settings.user.handle != "anonymous":
@@ -198,7 +311,7 @@ def _connect_instance(
198
311
  owner: str, # account_handle
199
312
  name: str, # instance_name
200
313
  client: Client,
201
- ) -> Union[Tuple[dict, dict], str]:
314
+ ) -> tuple[dict, dict] | str:
202
315
  instance_account_storage = select_instance_by_owner_name(owner, name, client)
203
316
  if instance_account_storage is None:
204
317
  # try the via single requests, will take more time
@@ -207,7 +320,7 @@ def _connect_instance(
207
320
  return "account-not-exists"
208
321
  instance = select_instance_by_name(account["id"], name, client)
209
322
  if instance is None:
210
- return "instance-not-reachable"
323
+ return "instance-not-found"
211
324
  # get default storage
212
325
  storage = select_storage(instance["storage_id"], client)
213
326
  if storage is None:
@@ -238,7 +351,7 @@ def _connect_instance(
238
351
  return instance, storage
239
352
 
240
353
 
241
- def access_aws(storage_root: str, access_token: Optional[str] = None) -> Dict[str, str]:
354
+ def access_aws(storage_root: str, access_token: str | None = None) -> dict[str, str]:
242
355
  from ._settings import settings
243
356
 
244
357
  if settings.user.handle != "anonymous" or access_token is not None:
@@ -250,7 +363,9 @@ def access_aws(storage_root: str, access_token: Optional[str] = None) -> Dict[st
250
363
  raise RuntimeError("Can only get access to AWS if authenticated.")
251
364
 
252
365
 
253
- def _access_aws(*, storage_root: str, client: Client) -> Dict[str, str]:
366
+ def _access_aws(*, storage_root: str, client: Client) -> dict[str, str]:
367
+ import lamindb_setup
368
+
254
369
  response = client.functions.invoke(
255
370
  "access-aws",
256
371
  invoke_options={"body": {"storage_root": storage_root}},
@@ -276,7 +391,7 @@ def get_lamin_site_base_url():
276
391
  return "https://lamin.ai"
277
392
 
278
393
 
279
- def sign_up_local_hub(email) -> Union[str, Tuple[str, str, str]]:
394
+ def sign_up_local_hub(email) -> str | tuple[str, str, str]:
280
395
  # raises gotrue.errors.AuthApiError: User already registered
281
396
  password = base62(40) # generate new password
282
397
  sign_up_kwargs = {"email": email, "password": password}
@@ -290,7 +405,7 @@ def sign_up_local_hub(email) -> Union[str, Tuple[str, str, str]]:
290
405
  )
291
406
 
292
407
 
293
- def _sign_in_hub(email: str, password: str, handle: Optional[str], client: Client):
408
+ def _sign_in_hub(email: str, password: str, handle: str | None, client: Client):
294
409
  auth = client.auth.sign_in_with_password(
295
410
  {
296
411
  "email": email,
@@ -320,8 +435,8 @@ def _sign_in_hub(email: str, password: str, handle: Optional[str], client: Clien
320
435
 
321
436
 
322
437
  def sign_in_hub(
323
- email: str, password: str, handle: Optional[str] = None
324
- ) -> Union[Exception, Tuple[UUID, str, str, str, str]]:
438
+ email: str, password: str, handle: str | None = None
439
+ ) -> Exception | tuple[UUID, str, str, str, str]:
325
440
  try:
326
441
  result = call_with_fallback(
327
442
  _sign_in_hub, email=email, password=password, handle=handle
@@ -1,20 +1,23 @@
1
- from supabase.client import Client
2
- from typing import Optional, Dict
3
- from lamin_utils import logger
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
4
  from uuid import UUID, uuid4
5
5
 
6
+ from lamin_utils import logger
7
+ from supabase.client import Client # noqa
8
+
6
9
 
7
10
  def select_instance_by_owner_name(
8
11
  owner: str,
9
12
  name: str,
10
13
  client: Client,
11
- ) -> Optional[Dict]:
14
+ ) -> dict | None:
12
15
  try:
13
16
  data = (
14
17
  client.table("instance")
15
18
  .select(
16
19
  "*, account!inner!instance_account_id_28936e8f_fk_account_id(*),"
17
- " storage(*)"
20
+ " storage!instance_storage_id_87963cc8_fk_storage_id(*)"
18
21
  )
19
22
  .eq("account.handle", owner)
20
23
  .eq("name", name)
@@ -82,7 +85,10 @@ def select_instance_by_id_with_storage(
82
85
  client: Client,
83
86
  ):
84
87
  response = (
85
- client.table("instance").select("*, storage(*)").eq("id", instance_id).execute()
88
+ client.table("instance")
89
+ .select("*, storage!instance_storage_id_87963cc8_fk_storage_id(*)")
90
+ .eq("id", instance_id)
91
+ .execute()
86
92
  )
87
93
  if len(response.data) == 0:
88
94
  return None
@@ -94,7 +100,7 @@ def update_instance(instance_id: str, instance_fields: dict, client: Client):
94
100
  client.table("instance").update(instance_fields).eq("id", instance_id).execute()
95
101
  )
96
102
  if len(response.data) == 0:
97
- raise RuntimeError(
103
+ raise PermissionError(
98
104
  f"Update of instance with {instance_id} was not successful. Probably, you"
99
105
  " don't have sufficient permissions."
100
106
  )
@@ -178,8 +184,11 @@ def select_db_user_by_instance(instance_id: str, client: Client):
178
184
  def _delete_instance_record(instance_id: UUID, client: Client) -> None:
179
185
  if not isinstance(instance_id, UUID):
180
186
  instance_id = UUID(instance_id)
181
- logger.important(f"deleting instance {instance_id.hex}")
182
- client.table("instance").delete().eq("id", instance_id.hex).execute()
183
-
184
-
185
- sb_delete_instance = _delete_instance_record
187
+ response = client.table("instance").delete().eq("id", instance_id.hex).execute()
188
+ if response.data:
189
+ logger.important(f"deleted instance record on hub {instance_id.hex}")
190
+ else:
191
+ raise PermissionError(
192
+ f"Deleting of instance with {instance_id.hex} was not successful. Probably, you"
193
+ " don't have sufficient permissions."
194
+ )
@@ -1,9 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  from typing import Optional
4
+
2
5
  from pydantic import BaseModel, validator
3
6
  from pydantic.networks import MultiHostDsn
4
7
 
5
8
 
6
- def validate_schema_arg(schema: Optional[str] = None) -> str:
9
+ def validate_schema_arg(schema: str | None = None) -> str:
7
10
  if schema is None or schema == "":
8
11
  return ""
9
12
  # currently no actual validation, can add back if we see a need
@@ -12,7 +15,7 @@ def validate_schema_arg(schema: Optional[str] = None) -> str:
12
15
  return ",".join(to_be_validated)
13
16
 
14
17
 
15
- def validate_db_arg(db: Optional[str]) -> None:
18
+ def validate_db_arg(db: str | None) -> None:
16
19
  if db is not None:
17
20
  LaminDsnModel(db=db)
18
21
 
@@ -43,13 +46,13 @@ class LaminDsn(MultiHostDsn):
43
46
  cls,
44
47
  *,
45
48
  scheme: str,
46
- user: Optional[str] = None,
47
- password: Optional[str] = None,
49
+ user: str | None = None,
50
+ password: str | None = None,
48
51
  host: str,
49
- port: Optional[str] = None,
50
- database: Optional[str] = None,
51
- query: Optional[str] = None,
52
- fragment: Optional[str] = None,
52
+ port: str | None = None,
53
+ database: str | None = None,
54
+ query: str | None = None,
55
+ fragment: str | None = None,
53
56
  **_kwargs: str,
54
57
  ) -> str:
55
58
  return super().build(
@@ -1,30 +1,30 @@
1
+ from __future__ import annotations
2
+
1
3
  import os
2
- from typing import Union, Optional
3
- from lamindb_setup.core import InstanceSettings, StorageSettings, UserSettings
4
- from lamindb_setup.core._settings_load import (
4
+ from typing import TYPE_CHECKING
5
+
6
+ from ._settings_load import (
5
7
  load_instance_settings,
6
8
  load_or_create_user_settings,
7
9
  )
8
- from ._settings_store import current_instance_settings_file
9
- from pathlib import Path
10
- from ._settings_store import settings_dir
10
+ from ._settings_store import current_instance_settings_file, settings_dir
11
11
 
12
+ if TYPE_CHECKING:
13
+ from pathlib import Path
12
14
 
13
- class SetupSettings:
14
- """Setup settings.
15
+ from lamindb_setup.core import InstanceSettings, StorageSettings, UserSettings
15
16
 
16
- - :class:`~lamindb_setup.core.InstanceSettings`
17
- - :class:`~lamindb_setup.core.StorageSettings`
18
- - :class:`~lamindb_setup.core.UserSettings`
19
- """
20
17
 
21
- _using_key: Optional[str] = None # set through lamindb.settings
18
+ class SetupSettings:
19
+ """Setup settings."""
22
20
 
23
- _user_settings: Union[UserSettings, None] = None
24
- _instance_settings: Union[InstanceSettings, None] = None
21
+ _using_key: str | None = None # set through lamindb.settings
25
22
 
26
- _user_settings_env: Union[str, None] = None
27
- _instance_settings_env: Union[str, None] = None
23
+ _user_settings: UserSettings | None = None
24
+ _instance_settings: InstanceSettings | None = None
25
+
26
+ _user_settings_env: str | None = None
27
+ _instance_settings_env: str | None = None
28
28
 
29
29
  _auto_connect_path: Path = settings_dir / "auto_connect"
30
30
 
@@ -50,11 +50,8 @@ class SetupSettings:
50
50
 
51
51
  @property
52
52
  def user(self) -> UserSettings:
53
- """User."""
54
- if (
55
- self._user_settings is None
56
- or self._user_settings_env != get_env_name() # noqa
57
- ):
53
+ """:class:`~lamindb.setup.core.UserSettings`."""
54
+ if self._user_settings is None or self._user_settings_env != get_env_name():
58
55
  self._user_settings = load_or_create_user_settings()
59
56
  self._user_settings_env = get_env_name()
60
57
  if self._user_settings and self._user_settings.uid is None:
@@ -63,10 +60,10 @@ class SetupSettings:
63
60
 
64
61
  @property
65
62
  def instance(self) -> InstanceSettings:
66
- """Instance."""
63
+ """:class:`~lamindb.setup.core.InstanceSettings`."""
67
64
  if (
68
65
  self._instance_settings is None
69
- or self._instance_settings_env != get_env_name() # noqa
66
+ or self._instance_settings_env != get_env_name()
70
67
  ):
71
68
  self._instance_settings = load_instance_settings()
72
69
  self._instance_settings_env = get_env_name()
@@ -74,13 +71,13 @@ class SetupSettings:
74
71
 
75
72
  @property
76
73
  def storage(self) -> StorageSettings:
77
- """Storage."""
74
+ """:class:`~lamindb.setup.core.StorageSettings`."""
78
75
  return self.instance.storage
79
76
 
80
77
  @property
81
78
  def _instance_exists(self):
82
79
  try:
83
- self.instance
80
+ self.instance # noqa
84
81
  return True
85
82
  # this is implicit logic that catches if no instance is loaded
86
83
  except SystemExit: