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.
Files changed (50) hide show
  1. lamindb_setup/__init__.py +4 -19
  2. lamindb_setup/_cache.py +87 -87
  3. lamindb_setup/_check.py +7 -7
  4. lamindb_setup/_check_setup.py +131 -131
  5. lamindb_setup/_connect_instance.py +443 -438
  6. lamindb_setup/_delete.py +155 -151
  7. lamindb_setup/_disconnect.py +38 -38
  8. lamindb_setup/_django.py +39 -39
  9. lamindb_setup/_entry_points.py +19 -19
  10. lamindb_setup/_init_instance.py +423 -429
  11. lamindb_setup/_migrate.py +331 -327
  12. lamindb_setup/_register_instance.py +32 -32
  13. lamindb_setup/_schema.py +27 -27
  14. lamindb_setup/_schema_metadata.py +451 -451
  15. lamindb_setup/_set_managed_storage.py +81 -80
  16. lamindb_setup/_setup_user.py +198 -198
  17. lamindb_setup/_silence_loggers.py +46 -46
  18. lamindb_setup/core/__init__.py +25 -34
  19. lamindb_setup/core/_aws_options.py +276 -266
  20. lamindb_setup/core/_aws_storage.py +57 -55
  21. lamindb_setup/core/_clone.py +50 -50
  22. lamindb_setup/core/_deprecated.py +62 -62
  23. lamindb_setup/core/_docs.py +14 -14
  24. lamindb_setup/core/_hub_client.py +288 -294
  25. lamindb_setup/core/_hub_core.py +0 -2
  26. lamindb_setup/core/_hub_crud.py +247 -247
  27. lamindb_setup/core/_hub_utils.py +100 -100
  28. lamindb_setup/core/_private_django_api.py +80 -80
  29. lamindb_setup/core/_settings.py +440 -434
  30. lamindb_setup/core/_settings_instance.py +32 -7
  31. lamindb_setup/core/_settings_load.py +162 -159
  32. lamindb_setup/core/_settings_save.py +108 -96
  33. lamindb_setup/core/_settings_storage.py +433 -433
  34. lamindb_setup/core/_settings_store.py +162 -92
  35. lamindb_setup/core/_settings_user.py +55 -55
  36. lamindb_setup/core/_setup_bionty_sources.py +44 -44
  37. lamindb_setup/core/cloud_sqlite_locker.py +240 -240
  38. lamindb_setup/core/django.py +414 -413
  39. lamindb_setup/core/exceptions.py +1 -1
  40. lamindb_setup/core/hashing.py +134 -134
  41. lamindb_setup/core/types.py +1 -1
  42. lamindb_setup/core/upath.py +1031 -1028
  43. lamindb_setup/errors.py +72 -70
  44. lamindb_setup/io.py +423 -416
  45. lamindb_setup/types.py +17 -17
  46. {lamindb_setup-1.18.2.dist-info → lamindb_setup-1.19.1.dist-info}/METADATA +4 -2
  47. lamindb_setup-1.19.1.dist-info/RECORD +51 -0
  48. {lamindb_setup-1.18.2.dist-info → lamindb_setup-1.19.1.dist-info}/WHEEL +1 -1
  49. {lamindb_setup-1.18.2.dist-info → lamindb_setup-1.19.1.dist-info/licenses}/LICENSE +201 -201
  50. lamindb_setup-1.18.2.dist-info/RECORD +0 -51
@@ -5,14 +5,8 @@ import shutil
5
5
  from pathlib import Path
6
6
  from typing import TYPE_CHECKING, Literal
7
7
 
8
- from django.db import connection
9
- from django.db.utils import ProgrammingError
10
8
  from lamin_utils import logger
11
9
 
12
- from ._deprecated import deprecated
13
- from ._hub_client import call_with_fallback
14
- from ._hub_crud import select_account_handle_name_by_lnid
15
- from ._hub_utils import LaminDsn, LaminDsnModel
16
10
  from ._settings_save import save_instance_settings
17
11
  from ._settings_storage import (
18
12
  LEGACY_STORAGE_UID_FILE_KEY,
@@ -85,6 +79,7 @@ class InstanceSettings:
85
79
  _locker_user: UserSettings | None = None, # user to lock for if cloud sqlite,
86
80
  _is_clone: bool = False,
87
81
  ):
82
+ # dynamic import to avoid importing pydantic at root
88
83
  from ._hub_utils import validate_db_arg
89
84
 
90
85
  self._id_: UUID = id
@@ -135,6 +130,9 @@ class InstanceSettings:
135
130
  )
136
131
  elif attr == "db":
137
132
  if self.dialect != "sqlite":
133
+ # dynamic import to avoid importing pydantic at root
134
+ from ._hub_utils import LaminDsn, LaminDsnModel
135
+
138
136
  model = LaminDsnModel(db=value)
139
137
  db_print = LaminDsn.build(
140
138
  scheme=model.db.scheme,
@@ -166,6 +164,7 @@ class InstanceSettings:
166
164
  def _search_local_root(
167
165
  self, local_root: str | None = None, mute_warning: bool = False
168
166
  ) -> StorageSettings | None:
167
+ from django.db.utils import ProgrammingError
169
168
  from lamindb.models import Storage
170
169
 
171
170
  if local_root is not None:
@@ -364,6 +363,28 @@ class InstanceSettings:
364
363
  else:
365
364
  return self.api_url.replace("/api", "")
366
365
 
366
+ @property
367
+ def is_read_only_connection(self) -> bool:
368
+ """Whether the current connection to the database is read-only."""
369
+ # should we check for self._is_clone?
370
+ if self.dialect == "postgresql":
371
+ db_url = self.db
372
+ if "read" in db_url or "public" in db_url:
373
+ return True
374
+ # works only for the default instance
375
+ if self._db_permissions == "jwt":
376
+ from ._settings import settings
377
+
378
+ db_token = settings._get_db_token()
379
+ # should not happen
380
+ if db_token is None:
381
+ logger.warning("DB token is not set")
382
+ return False
383
+ # inits or refreshes the token if needed
384
+ db_token.token_query # noqa: B018
385
+ return db_token._type == "read-only"
386
+ return False
387
+
367
388
  @property
368
389
  def available_spaces(self) -> dict | None:
369
390
  """Available spaces with roles for instances fine-grained permissions.
@@ -375,6 +396,7 @@ class InstanceSettings:
375
396
  if self._db_permissions != "jwt":
376
397
  return None
377
398
 
399
+ from django.db import connection
378
400
  from lamindb.models import Space
379
401
 
380
402
  spaces: dict = {"admin": [], "write": [], "read": []}
@@ -452,6 +474,9 @@ class InstanceSettings:
452
474
  sqlite_file.synchronize_to(cache_file, print_progress=True) # type: ignore
453
475
 
454
476
  def _check_sqlite_lock(self):
477
+ from ._hub_client import call_with_fallback
478
+ from ._hub_crud import select_account_handle_name_by_lnid
479
+
455
480
  if not self._cloud_sqlite_locker.has_lock:
456
481
  locked_by = self._cloud_sqlite_locker._locked_by
457
482
  lock_msg = "Cannot load the instance, it is locked by "
@@ -560,7 +585,7 @@ class InstanceSettings:
560
585
  Will return `False` in case the user token can't find the instance.
561
586
  """
562
587
  if self._is_on_hub is None:
563
- from ._hub_client import call_with_fallback_auth
588
+ from ._hub_client import call_with_fallback, call_with_fallback_auth
564
589
  from ._hub_crud import select_instance_by_id
565
590
  from ._settings import settings
566
591
 
@@ -1,159 +1,162 @@
1
- from __future__ import annotations
2
-
3
- import os
4
- from importlib.util import find_spec
5
- from pathlib import Path
6
- from typing import TYPE_CHECKING
7
- from uuid import UUID, uuid4
8
-
9
- from dotenv import dotenv_values
10
- from lamin_utils import logger
11
- from pydantic import ValidationError
12
-
13
- from lamindb_setup.errors import CurrentInstanceNotConfigured, SettingsEnvFileOutdated
14
-
15
- from ._settings_instance import InstanceSettings
16
- from ._settings_storage import StorageSettings
17
- from ._settings_store import (
18
- InstanceSettingsStore,
19
- UserSettingsStore,
20
- current_instance_settings_file,
21
- current_user_settings_file,
22
- platform_user_storage_settings_file,
23
- system_settings_file,
24
- )
25
- from ._settings_user import UserSettings
26
-
27
-
28
- def load_cache_path_from_settings(storage_settings: Path | None = None) -> Path | None:
29
- if storage_settings is None:
30
- paltform_user_storage_settings = platform_user_storage_settings_file()
31
- if paltform_user_storage_settings.exists():
32
- cache_path = dotenv_values(paltform_user_storage_settings).get(
33
- "lamindb_cache_path", None
34
- )
35
- else:
36
- cache_path = None
37
-
38
- if cache_path in {None, "null", ""}:
39
- storage_settings = system_settings_file()
40
- else:
41
- return Path(cache_path)
42
-
43
- if storage_settings.exists():
44
- cache_path = dotenv_values(storage_settings).get("lamindb_cache_path", None)
45
- return Path(cache_path) if cache_path not in {None, "null", ""} else None
46
- else:
47
- return None
48
-
49
-
50
- def find_module_candidates():
51
- """Find all local packages that depend on lamindb."""
52
- candidates = ["bionty", "pertdb"]
53
- return [c for c in candidates if find_spec(c) is not None]
54
-
55
-
56
- def load_instance_settings(instance_settings_file: Path | None = None):
57
- if instance_settings_file is None:
58
- isettings_file = current_instance_settings_file()
59
- if not isettings_file.exists():
60
- isettings = InstanceSettings(
61
- id=UUID("00000000-0000-0000-0000-000000000000"),
62
- owner="none",
63
- name="none",
64
- storage=None,
65
- modules=",".join(find_module_candidates()),
66
- )
67
- return isettings
68
- else:
69
- isettings_file = instance_settings_file
70
-
71
- if not isettings_file.exists():
72
- # this errors only if the file was explicitly provided
73
- raise CurrentInstanceNotConfigured
74
- try:
75
- settings_store = InstanceSettingsStore(_env_file=isettings_file)
76
- except (ValidationError, TypeError) as error:
77
- raise SettingsEnvFileOutdated(
78
- f"\n\n{error}\n\nYour instance settings file with\n\n{isettings_file.read_text()}\nis invalid"
79
- f" (likely outdated), see validation error. Please delete {isettings_file} &"
80
- " reload (remote) or re-initialize (local) the instance with the same name & storage location."
81
- ) from error
82
- isettings = setup_instance_from_store(settings_store)
83
- return isettings
84
-
85
-
86
- def load_or_create_user_settings(api_key: str | None = None) -> UserSettings:
87
- """Return current user settings.
88
-
89
- Args:
90
- api_key: if provided and there is no current user, perform login and return the user settings.
91
- """
92
- current_user_settings = current_user_settings_file()
93
- if not current_user_settings.exists():
94
- if api_key is not None:
95
- from lamindb_setup._setup_user import login
96
-
97
- return login(api_key=api_key)
98
- else:
99
- logger.warning("using anonymous user (to identify, call: lamin login)")
100
- usettings = UserSettings(handle="anonymous", uid="00000000")
101
- from ._settings_save import save_user_settings
102
-
103
- save_user_settings(usettings)
104
- else:
105
- usettings = load_user_settings(current_user_settings)
106
- return usettings
107
-
108
-
109
- def load_user_settings(user_settings_file: Path):
110
- try:
111
- settings_store = UserSettingsStore(_env_file=user_settings_file)
112
- except (ValidationError, TypeError) as error:
113
- msg = (
114
- "Your user settings file is invalid, please delete"
115
- f" {user_settings_file} and log in again."
116
- )
117
- print(msg)
118
- raise SettingsEnvFileOutdated(msg) from error
119
- settings = setup_user_from_store(settings_store)
120
- return settings
121
-
122
-
123
- def _null_to_value(field, value=None):
124
- return field if field != "null" else value
125
-
126
-
127
- def setup_instance_from_store(store: InstanceSettingsStore) -> InstanceSettings:
128
- ssettings = StorageSettings(
129
- root=store.storage_root,
130
- region=_null_to_value(store.storage_region),
131
- )
132
- return InstanceSettings(
133
- id=UUID(store.id),
134
- owner=store.owner,
135
- name=store.name,
136
- storage=ssettings,
137
- db=_null_to_value(store.db),
138
- modules=_null_to_value(store.schema_str),
139
- git_repo=_null_to_value(store.git_repo),
140
- keep_artifacts_local=store.keep_artifacts_local, # type: ignore
141
- api_url=_null_to_value(store.api_url),
142
- schema_id=None if store.schema_id in {None, "null"} else UUID(store.schema_id),
143
- fine_grained_access=store.fine_grained_access,
144
- db_permissions=_null_to_value(store.db_permissions),
145
- _is_clone=store.is_clone,
146
- )
147
-
148
-
149
- def setup_user_from_store(store: UserSettingsStore) -> UserSettings:
150
- settings = UserSettings()
151
- settings.email = _null_to_value(store.email)
152
- settings.password = _null_to_value(store.password)
153
- settings.access_token = store.access_token
154
- settings.api_key = _null_to_value(store.api_key)
155
- settings.uid = store.uid
156
- settings.handle = _null_to_value(store.handle, value="anonymous")
157
- settings.name = _null_to_value(store.name)
158
- settings._uuid = UUID(store.uuid) if store.uuid != "null" else None
159
- return settings
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from importlib.util import find_spec
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING
7
+ from uuid import UUID, uuid4
8
+
9
+ from dotenv import dotenv_values
10
+ from lamin_utils import logger
11
+
12
+ from lamindb_setup.errors import CurrentInstanceNotConfigured, SettingsEnvFileOutdated
13
+
14
+ from ._settings_instance import InstanceSettings
15
+ from ._settings_storage import StorageSettings
16
+ from ._settings_store import (
17
+ InstanceSettingsStore,
18
+ UserSettingsStore,
19
+ current_instance_settings_file,
20
+ current_user_settings_file,
21
+ platform_user_storage_settings_file,
22
+ system_settings_file,
23
+ )
24
+ from ._settings_user import UserSettings
25
+
26
+
27
+ def load_cache_path_from_settings(storage_settings: Path | None = None) -> Path | None:
28
+ if storage_settings is None:
29
+ paltform_user_storage_settings = platform_user_storage_settings_file()
30
+ if paltform_user_storage_settings.exists():
31
+ cache_path = dotenv_values(paltform_user_storage_settings).get(
32
+ "lamindb_cache_path", None
33
+ )
34
+ else:
35
+ cache_path = None
36
+
37
+ if cache_path in {None, "null", ""}:
38
+ storage_settings = system_settings_file()
39
+ else:
40
+ return Path(cache_path)
41
+
42
+ if storage_settings.exists():
43
+ cache_path = dotenv_values(storage_settings).get("lamindb_cache_path", None)
44
+ return Path(cache_path) if cache_path not in {None, "null", ""} else None
45
+ else:
46
+ return None
47
+
48
+
49
+ def find_module_candidates():
50
+ """Find all local packages that depend on lamindb."""
51
+ candidates = ["bionty", "pertdb"]
52
+ return [c for c in candidates if find_spec(c) is not None]
53
+
54
+
55
+ def load_instance_settings(instance_settings_file: Path | None = None):
56
+ if instance_settings_file is None:
57
+ isettings_file = current_instance_settings_file()
58
+ if not isettings_file.exists():
59
+ isettings = InstanceSettings(
60
+ id=UUID("00000000-0000-0000-0000-000000000000"),
61
+ owner="none",
62
+ name="none",
63
+ storage=None,
64
+ modules=",".join(find_module_candidates()),
65
+ )
66
+ return isettings
67
+ else:
68
+ isettings_file = instance_settings_file
69
+
70
+ if not isettings_file.exists():
71
+ # this errors only if the file was explicitly provided
72
+ raise CurrentInstanceNotConfigured
73
+ try:
74
+ settings_store = InstanceSettingsStore.from_env_file(
75
+ isettings_file, "lamindb_instance_"
76
+ )
77
+ except (ValueError, KeyError, TypeError) as error:
78
+ raise SettingsEnvFileOutdated(
79
+ f"\n\n{error}\n\nYour instance settings file with\n\n{isettings_file.read_text()}\nis invalid"
80
+ f" (likely outdated), see validation error. Please delete {isettings_file} &"
81
+ " reload (remote) or re-initialize (local) the instance with the same name & storage location."
82
+ ) from error
83
+ isettings = setup_instance_from_store(settings_store)
84
+ return isettings
85
+
86
+
87
+ def load_or_create_user_settings(api_key: str | None = None) -> UserSettings:
88
+ """Return current user settings.
89
+
90
+ Args:
91
+ api_key: if provided and there is no current user, perform login and return the user settings.
92
+ """
93
+ current_user_settings = current_user_settings_file()
94
+ if not current_user_settings.exists():
95
+ if api_key is not None:
96
+ from lamindb_setup._setup_user import login
97
+
98
+ return login(api_key=api_key)
99
+ else:
100
+ logger.warning("using anonymous user (to identify, call: lamin login)")
101
+ usettings = UserSettings(handle="anonymous", uid="00000000")
102
+ from ._settings_save import save_user_settings
103
+
104
+ save_user_settings(usettings)
105
+ else:
106
+ usettings = load_user_settings(current_user_settings)
107
+ return usettings
108
+
109
+
110
+ def load_user_settings(user_settings_file: Path):
111
+ try:
112
+ settings_store = UserSettingsStore.from_env_file(
113
+ user_settings_file, "lamin_user_"
114
+ )
115
+ except (ValueError, KeyError, TypeError) as error:
116
+ msg = (
117
+ "Your user settings file is invalid, please delete"
118
+ f" {user_settings_file} and log in again."
119
+ )
120
+ print(msg)
121
+ raise SettingsEnvFileOutdated(msg) from error
122
+ settings = setup_user_from_store(settings_store)
123
+ return settings
124
+
125
+
126
+ def _null_to_value(field, value=None):
127
+ return value if field in (None, "null") else field
128
+
129
+
130
+ def setup_instance_from_store(store: InstanceSettingsStore) -> InstanceSettings:
131
+ ssettings = StorageSettings(
132
+ root=store.storage_root,
133
+ region=_null_to_value(store.storage_region),
134
+ )
135
+ return InstanceSettings(
136
+ id=UUID(store.id),
137
+ owner=store.owner,
138
+ name=store.name,
139
+ storage=ssettings,
140
+ db=_null_to_value(store.db),
141
+ modules=_null_to_value(store.schema_str),
142
+ git_repo=_null_to_value(store.git_repo),
143
+ keep_artifacts_local=store.keep_artifacts_local, # type: ignore
144
+ api_url=_null_to_value(store.api_url),
145
+ schema_id=None if store.schema_id in {None, "null"} else UUID(store.schema_id),
146
+ fine_grained_access=store.fine_grained_access,
147
+ db_permissions=_null_to_value(store.db_permissions),
148
+ _is_clone=store.is_clone,
149
+ )
150
+
151
+
152
+ def setup_user_from_store(store: UserSettingsStore) -> UserSettings:
153
+ settings = UserSettings()
154
+ settings.email = _null_to_value(store.email)
155
+ settings.password = _null_to_value(store.password)
156
+ settings.access_token = store.access_token
157
+ settings.api_key = _null_to_value(store.api_key)
158
+ settings.uid = store.uid
159
+ settings.handle = _null_to_value(store.handle, value="anonymous")
160
+ settings.name = _null_to_value(store.name)
161
+ settings._uuid = UUID(store.uuid) if store.uuid not in (None, "null") else None
162
+ return settings
@@ -1,96 +1,108 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
- from typing import TYPE_CHECKING, Any, Optional, get_type_hints
5
- from uuid import UUID
6
-
7
- from ._settings_store import (
8
- InstanceSettingsStore,
9
- UserSettingsStore,
10
- current_user_settings_file,
11
- platform_user_storage_settings_file,
12
- user_settings_file_email,
13
- user_settings_file_handle,
14
- )
15
-
16
- if TYPE_CHECKING:
17
- from lamindb_setup.types import UPathStr
18
-
19
- from ._settings_user import UserSettings
20
-
21
-
22
- def save_user_settings(settings: UserSettings):
23
- type_hints = get_type_hints(UserSettingsStore)
24
- prefix = "lamin_user_"
25
- save_settings(settings, current_user_settings_file(), type_hints, prefix)
26
- if settings.email is not None:
27
- save_settings(
28
- settings, user_settings_file_email(settings.email), type_hints, prefix
29
- )
30
- if settings.handle is not None and settings.handle != "anonymous":
31
- save_settings(
32
- settings, user_settings_file_handle(settings.handle), type_hints, prefix
33
- )
34
-
35
-
36
- def save_settings(
37
- settings: Any,
38
- settings_file: Path,
39
- type_hints: dict[str, Any],
40
- prefix: str,
41
- ):
42
- with open(settings_file, "w") as f:
43
- for store_key, type_ in type_hints.items():
44
- if type_ == Optional[str]: # noqa: UP045
45
- type_ = str
46
- if type_ == Optional[bool]: # noqa: UP045
47
- type_ = bool
48
- if "__" not in store_key:
49
- if store_key == "model_config":
50
- continue
51
- if store_key == "storage_root":
52
- value = settings.storage.root_as_str
53
- elif store_key == "storage_region":
54
- value = settings.storage.region
55
- else:
56
- if store_key in {
57
- "db",
58
- "schema_str",
59
- "name_",
60
- "uuid",
61
- "id",
62
- "api_url",
63
- "schema_id",
64
- "fine_grained_access",
65
- "db_permissions",
66
- "is_clone",
67
- }:
68
- settings_key = f"_{store_key.rstrip('_')}"
69
- else:
70
- settings_key = store_key
71
- value = getattr(settings, settings_key, None)
72
- if value is None:
73
- value = "null"
74
- elif isinstance(value, UUID):
75
- value = value.hex
76
- else:
77
- value = type_(value)
78
- f.write(f"{prefix}{store_key}={value}\n")
79
-
80
-
81
- def save_instance_settings(settings: Any, settings_file: Path):
82
- type_hints = get_type_hints(InstanceSettingsStore)
83
- prefix = "lamindb_instance_"
84
- save_settings(settings, settings_file, type_hints, prefix)
85
-
86
-
87
- def save_platform_user_storage_settings(
88
- cache_path: UPathStr | None, settings_file: UPathStr | None = None
89
- ):
90
- cache_path = "null" if cache_path is None else cache_path
91
- if isinstance(cache_path, Path): # also True for UPath
92
- cache_path = cache_path.as_posix()
93
- if settings_file is None:
94
- settings_file = platform_user_storage_settings_file()
95
- with open(settings_file, "w") as f:
96
- f.write(f"lamindb_cache_path={cache_path}")
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+ from typing import TYPE_CHECKING, Any, Optional, get_args, get_type_hints
5
+ from uuid import UUID
6
+
7
+ from ._settings_store import (
8
+ InstanceSettingsStore,
9
+ UserSettingsStore,
10
+ current_user_settings_file,
11
+ platform_user_storage_settings_file,
12
+ user_settings_file_email,
13
+ user_settings_file_handle,
14
+ )
15
+
16
+ if TYPE_CHECKING:
17
+ from lamindb_setup.types import UPathStr
18
+
19
+ from ._settings_user import UserSettings
20
+
21
+
22
+ def save_user_settings(settings: UserSettings):
23
+ type_hints = get_type_hints(UserSettingsStore)
24
+ prefix = "lamin_user_"
25
+ save_settings(settings, current_user_settings_file(), type_hints, prefix)
26
+ if settings.email is not None:
27
+ save_settings(
28
+ settings, user_settings_file_email(settings.email), type_hints, prefix
29
+ )
30
+ if settings.handle is not None and settings.handle != "anonymous":
31
+ save_settings(
32
+ settings, user_settings_file_handle(settings.handle), type_hints, prefix
33
+ )
34
+
35
+
36
+ def _coerce_type_for_write(type_: Any) -> Any:
37
+ """Resolve union types to the non-None part for coercion when value is not None."""
38
+ if type_ in (str, bool):
39
+ return type_
40
+ if type_ == Optional[str]: # noqa: UP045
41
+ return str
42
+ if type_ == Optional[bool]: # noqa: UP045
43
+ return bool
44
+ args = get_args(type_) or ()
45
+ if type(None) in args:
46
+ non_none = next((a for a in args if a is not type(None)), type_)
47
+ return non_none if non_none in (str, bool) else type_
48
+ return type_
49
+
50
+
51
+ def save_settings(
52
+ settings: Any,
53
+ settings_file: Path,
54
+ type_hints: dict[str, Any],
55
+ prefix: str,
56
+ ):
57
+ with open(settings_file, "w") as f:
58
+ for store_key, type_ in type_hints.items():
59
+ type_ = _coerce_type_for_write(type_)
60
+ if "__" not in store_key:
61
+ if store_key == "model_config":
62
+ continue
63
+ if store_key == "storage_root":
64
+ value = settings.storage.root_as_str
65
+ elif store_key == "storage_region":
66
+ value = settings.storage.region
67
+ else:
68
+ if store_key in {
69
+ "db",
70
+ "schema_str",
71
+ "name_",
72
+ "uuid",
73
+ "id",
74
+ "api_url",
75
+ "schema_id",
76
+ "fine_grained_access",
77
+ "db_permissions",
78
+ "is_clone",
79
+ }:
80
+ settings_key = f"_{store_key.rstrip('_')}"
81
+ else:
82
+ settings_key = store_key
83
+ value = getattr(settings, settings_key, None)
84
+ if value is None:
85
+ value = "null"
86
+ elif isinstance(value, UUID):
87
+ value = value.hex
88
+ else:
89
+ value = type_(value)
90
+ f.write(f"{prefix}{store_key}={value}\n")
91
+
92
+
93
+ def save_instance_settings(settings: Any, settings_file: Path):
94
+ type_hints = get_type_hints(InstanceSettingsStore)
95
+ prefix = "lamindb_instance_"
96
+ save_settings(settings, settings_file, type_hints, prefix)
97
+
98
+
99
+ def save_platform_user_storage_settings(
100
+ cache_path: UPathStr | None, settings_file: UPathStr | None = None
101
+ ):
102
+ cache_path = "null" if cache_path is None else cache_path
103
+ if isinstance(cache_path, Path): # also True for UPath
104
+ cache_path = cache_path.as_posix()
105
+ if settings_file is None:
106
+ settings_file = platform_user_storage_settings_file()
107
+ with open(settings_file, "w") as f:
108
+ f.write(f"lamindb_cache_path={cache_path}")