lamindb_setup 1.15.2__py3-none-any.whl → 1.17.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 CHANGED
@@ -35,14 +35,17 @@ Migration management
35
35
 
36
36
  """
37
37
 
38
- __version__ = "1.15.2" # denote a release candidate for 0.1.0 with 0.1rc1
38
+ __version__ = "1.17.0" # denote a release candidate for 0.1.0 with 0.1rc1
39
39
 
40
40
  import os
41
41
  import warnings
42
42
 
43
- # ignore for now, remove this after supabase upgrade in the deps
43
+ # ignore for now, this is for timeout parameter,
44
+ # it is more convenient to specify it directly for now
44
45
  warnings.filterwarnings("ignore", category=DeprecationWarning, module="supabase")
45
- warnings.filterwarnings("ignore", category=DeprecationWarning, module="supafunc")
46
+ warnings.filterwarnings(
47
+ "ignore", category=DeprecationWarning, module="supabase_functions"
48
+ )
46
49
  warnings.filterwarnings("ignore", category=DeprecationWarning, module="postgrest")
47
50
 
48
51
  from packaging import version as packaging_version
@@ -4,7 +4,6 @@ import functools
4
4
  import importlib as il
5
5
  import inspect
6
6
  import os
7
- from importlib.metadata import distributions
8
7
  from typing import TYPE_CHECKING
9
8
  from uuid import UUID
10
9
 
@@ -16,7 +15,6 @@ from .core._settings import settings
16
15
  from .core._settings_store import current_instance_settings_file
17
16
  from .errors import (
18
17
  MODULE_WASNT_CONFIGURED_MESSAGE_TEMPLATE,
19
- InstanceNotSetupError,
20
18
  ModuleWasntConfigured,
21
19
  )
22
20
 
@@ -26,8 +24,6 @@ if TYPE_CHECKING:
26
24
  from .core._settings_instance import InstanceSettings
27
25
 
28
26
 
29
- CURRENT_ISETTINGS: InstanceSettings | None = None
30
- MODULE_CANDIDATES: set[str] | None = None
31
27
  IS_LOADING: bool = False
32
28
 
33
29
 
@@ -45,55 +41,6 @@ def disable_auto_connect(func: Callable):
45
41
  return wrapper
46
42
 
47
43
 
48
- def find_module_candidates():
49
- """Find all local packages that depend on lamindb."""
50
- global MODULE_CANDIDATES
51
- if MODULE_CANDIDATES is not None:
52
- return MODULE_CANDIDATES
53
- all_dists = list(distributions())
54
- lamindb_deps = {
55
- dist.metadata["Name"].lower()
56
- for dist in all_dists
57
- if dist.requires and any("lamindb" in req.lower() for req in dist.requires)
58
- }
59
- lamindb_deps.remove("lamindb")
60
- MODULE_CANDIDATES = lamindb_deps
61
- return lamindb_deps
62
-
63
-
64
- def _get_current_instance_settings(from_module: str | None = None) -> InstanceSettings:
65
- from .core._settings_instance import InstanceSettings
66
-
67
- global CURRENT_ISETTINGS
68
-
69
- if CURRENT_ISETTINGS is not None:
70
- return CURRENT_ISETTINGS
71
- if current_instance_settings_file().exists():
72
- from .core._settings_load import load_instance_settings
73
-
74
- try:
75
- isettings = load_instance_settings()
76
- except Exception as e:
77
- # user will get more detailed traceback once they run the CLI
78
- logger.error(
79
- "Current instance cannot be reached, disconnect from it: `lamin disconnect`\n"
80
- "Alternatively, init or load a connectable instance on the"
81
- " command line: `lamin connect <instance>` or `lamin init <...>`"
82
- )
83
- raise e
84
- else:
85
- module_candidates = find_module_candidates()
86
- isettings = InstanceSettings(
87
- id=UUID("00000000-0000-0000-0000-000000000000"),
88
- owner="none",
89
- name="none",
90
- storage=None,
91
- modules=",".join(module_candidates),
92
- )
93
- CURRENT_ISETTINGS = isettings
94
- return isettings
95
-
96
-
97
44
  def _normalize_module_name(module_name: str) -> str:
98
45
  return module_name.replace("lnschema_", "").replace("_", "-")
99
46
 
@@ -137,12 +84,9 @@ def _infer_callers_module_name() -> str | None:
137
84
  # users should not see it
138
85
  def _check_instance_setup(from_module: str | None = None) -> bool:
139
86
  if django_lamin.IS_SETUP:
140
- # reload logic here because module might not yet have been imported
141
- # upon first setup
142
87
  if from_module is not None:
143
88
  if from_module != "lamindb":
144
89
  _check_module_in_instance_modules(from_module)
145
- il.reload(il.import_module(from_module))
146
90
  else:
147
91
  infer_module = _infer_callers_module_name()
148
92
  if infer_module is not None and infer_module not in {
@@ -159,34 +103,29 @@ def _check_instance_setup(from_module: str | None = None) -> bool:
159
103
  "errors in regular lamindb usage"
160
104
  )
161
105
  return True
162
- isettings = _get_current_instance_settings()
163
- if isettings is not None:
164
- if from_module is not None and not django_lamin.IS_SETUP and not IS_LOADING:
165
- if from_module != "lamindb":
166
- _check_module_in_instance_modules(from_module, isettings)
167
-
168
- import lamindb
169
-
170
- il.reload(il.import_module(from_module))
171
- else:
172
- django_lamin.setup_django(isettings)
173
- if isettings.slug != "none/none":
174
- logger.important(f"connected lamindb: {isettings.slug}")
175
- # update of local storage location through search_local_root()
176
- settings._instance_settings = isettings
177
- else:
178
- logger.warning("not connected, call: ln.connect('account/name')")
106
+
107
+ if IS_LOADING or from_module is None:
108
+ return False
109
+
110
+ if (
111
+ not settings._instance_exists
112
+ and os.environ.get("LAMIN_CURRENT_INSTANCE") is not None
113
+ ):
114
+ from ._connect_instance import connect
115
+
116
+ connect(_write_settings=False, _reload_lamindb=False)
179
117
  return django_lamin.IS_SETUP
180
118
  else:
181
- if from_module is not None:
182
- # the below enables users to auto-connect to an instance
183
- # simply by setting an environment variable, bypassing the
184
- # need of calling connect() manually
185
- if os.environ.get("LAMIN_CURRENT_INSTANCE") is not None:
186
- from ._connect_instance import connect
187
-
188
- connect(_write_settings=False, _reload_lamindb=False)
189
- return django_lamin.IS_SETUP
190
- else:
191
- logger.warning(InstanceNotSetupError.default_message)
192
- return False
119
+ isettings = settings.instance
120
+ if from_module != "lamindb":
121
+ _check_module_in_instance_modules(from_module, isettings)
122
+
123
+ import lamindb # connect to the instance
124
+ else:
125
+ # disable_auto_connect to avoid triggering _check_instance_setup in modules
126
+ disable_auto_connect(django_lamin.setup_django)(isettings)
127
+ if isettings.slug != "none/none":
128
+ logger.important(f"connected lamindb: {isettings.slug}")
129
+ # update of local storage location through search_local_root()
130
+ settings._instance_settings = isettings
131
+ return django_lamin.IS_SETUP
@@ -9,10 +9,7 @@ from uuid import UUID
9
9
 
10
10
  from lamin_utils import logger
11
11
 
12
- from ._check_setup import (
13
- _check_instance_setup,
14
- _get_current_instance_settings,
15
- )
12
+ from ._check_setup import _check_instance_setup
16
13
  from ._disconnect import disconnect
17
14
  from ._init_instance import load_from_isettings
18
15
  from ._silence_loggers import silence_loggers
@@ -266,13 +263,8 @@ def validate_connection_state(
266
263
  from django.db import connection
267
264
 
268
265
  if (
269
- settings._instance_exists
266
+ settings._instance_exists # exists only for real instances, not for none/none
270
267
  and f"{owner}/{name}" == settings.instance.slug
271
- # below is to ensure that if another process interferes
272
- # we don't use the in-memory mock database
273
- # could be made more specific by checking whether the django
274
- # configured database is the same as the one in settings
275
- and connection.settings_dict["NAME"] != ":memory:"
276
268
  and not use_root_db_user # always re-connect for root db user
277
269
  ):
278
270
  logger.important(
@@ -280,7 +272,7 @@ def validate_connection_state(
280
272
  )
281
273
  return None
282
274
  else:
283
- if settings._instance_exists and settings.instance.slug != "none/none":
275
+ if settings._instance_exists:
284
276
  import lamindb as ln
285
277
 
286
278
  if ln.context.transform is not None:
@@ -292,7 +284,9 @@ def validate_connection_state(
292
284
 
293
285
  @unlock_cloud_sqlite_upon_exception(ignore_prev_locker=True)
294
286
  def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
295
- """Connect to an instance.
287
+ """Connect the global default instance.
288
+
289
+ If you want to create a read-only database client, use :class:`~lamindb.DB` instead.
296
290
 
297
291
  Args:
298
292
  instance: Pass a slug (`account/name`) or URL (`https://lamin.ai/account/name`).
@@ -340,12 +334,9 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
340
334
  if settings._instance_exists:
341
335
  isettings = settings.instance
342
336
  else:
343
- isettings_or_none = _get_current_instance_settings()
344
- if isettings_or_none is None:
345
- raise ValueError(
346
- "No instance was connected through the CLI, pass a value to `instance` or connect via the CLI."
347
- )
348
- isettings = isettings_or_none
337
+ raise ValueError(
338
+ "No instance was connected through the CLI, pass a value to `instance` or connect via the CLI."
339
+ )
349
340
  if use_root_db_user:
350
341
  reset_django()
351
342
  owner, name = isettings.owner, isettings.name
@@ -414,7 +405,6 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
414
405
 
415
406
  load_from_isettings(isettings, user=_user, write_settings=_write_settings)
416
407
  if _reload_lamindb:
417
- importlib.reload(importlib.import_module("lamindb"))
418
408
  reset_django_module_variables()
419
409
  if isettings.slug != "none/none":
420
410
  logger.important(f"connected lamindb: {isettings.slug}")
@@ -424,10 +414,6 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
424
414
  isettings._get_settings_file().unlink(missing_ok=True) # type: ignore
425
415
  settings._instance_settings = None
426
416
  raise e
427
- if settings.dev_dir is None:
428
- logger.important_hint(
429
- "to map a local dev directory, set: ln.setup.settings.dev_dir = '.'"
430
- )
431
417
  return None
432
418
 
433
419
 
lamindb_setup/_delete.py CHANGED
@@ -11,6 +11,7 @@ from .core._aws_options import HOSTED_BUCKETS
11
11
  from .core._hub_core import delete_instance as delete_instance_on_hub
12
12
  from .core._hub_core import get_storage_records_for_instance
13
13
  from .core._settings import settings
14
+ from .core._settings_load import load_instance_settings
14
15
  from .core._settings_storage import StorageSettings
15
16
  from .core.upath import LocalPathClasses, check_storage_is_empty
16
17
 
@@ -38,6 +39,8 @@ def delete_exclusion_dir(isettings: InstanceSettings) -> None:
38
39
 
39
40
 
40
41
  def delete_by_isettings(isettings: InstanceSettings) -> None:
42
+ assert isettings.slug != "none/none"
43
+
41
44
  settings_file = isettings._get_settings_file()
42
45
  if settings_file.exists():
43
46
  settings_file.unlink()
@@ -51,12 +54,14 @@ def delete_by_isettings(isettings: InstanceSettings) -> None:
51
54
  "Did not have permission to delete SQLite file:"
52
55
  f" {isettings._sqlite_file}"
53
56
  )
54
- pass
55
57
  # unset the global instance settings
56
- if settings._instance_exists and isettings.slug == settings.instance.slug:
57
- if settings._instance_settings_path.exists():
58
- settings._instance_settings_path.unlink()
59
- settings._instance_settings = None
58
+ isettings_on_disk = load_instance_settings()
59
+ if isettings_on_disk.slug == isettings.slug:
60
+ settings._instance_settings_path.unlink() # current instance settings file
61
+ # settings.instance can differ from instance in current_settings_file()
62
+ # due to connect() in the same process
63
+ if settings.instance.slug == isettings.slug:
64
+ settings._instance_settings = None
60
65
 
61
66
 
62
67
  def delete(slug: str, force: bool = False, require_empty: bool = True) -> int | None:
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from lamin_utils import logger
4
4
 
5
5
  from .core._settings import settings
6
- from .core._settings_store import current_instance_settings_file
6
+ from .core._settings_load import load_instance_settings
7
7
  from .core.cloud_sqlite_locker import clear_locker
8
8
 
9
9
 
@@ -15,10 +15,11 @@ def disconnect(mute: bool = False) -> None:
15
15
  See Also:
16
16
  Clear default instance configuration via the CLI, see `here <https://docs.lamin.ai/cli#disconnect>`__.
17
17
  """
18
- if current_instance_settings_file().exists():
19
- instance = settings.instance.slug
18
+ # settings._instance_exists can be true due to connect even without having a file
19
+ if settings._instance_exists:
20
+ instance = settings.instance
20
21
  try:
21
- settings.instance._update_cloud_sqlite_file()
22
+ instance._update_cloud_sqlite_file()
22
23
  except Exception as e:
23
24
  if isinstance(e, FileNotFoundError):
24
25
  logger.warning("did not find local cache file")
@@ -26,10 +27,12 @@ def disconnect(mute: bool = False) -> None:
26
27
  logger.warning("did not upload cache file - not enough permissions")
27
28
  else:
28
29
  raise e
29
- current_instance_settings_file().unlink()
30
30
  clear_locker()
31
+ # instance in current instance file can differ from instance in settings
32
+ if load_instance_settings().slug == instance.slug:
33
+ settings._instance_settings_path.unlink(missing_ok=True)
34
+ settings._instance_settings = None
31
35
  if not mute:
32
- logger.success(f"disconnected instance: {instance}")
33
- else:
34
- if not mute:
35
- logger.info("no instance loaded")
36
+ logger.success(f"disconnected instance: {instance.slug}")
37
+ elif not mute:
38
+ logger.info("no instance loaded")
@@ -340,7 +340,6 @@ def init(
340
340
  from ._schema_metadata import update_schema_in_hub
341
341
 
342
342
  update_schema_in_hub(access_token=access_token)
343
- importlib.reload(importlib.import_module("lamindb"))
344
343
  reset_django_module_variables()
345
344
  logger.important(f"initialized lamindb: {isettings.slug}")
346
345
  except Exception as e:
lamindb_setup/_migrate.py CHANGED
@@ -125,24 +125,10 @@ class migrate:
125
125
  from lamindb_setup._schema_metadata import update_schema_in_hub
126
126
  from lamindb_setup.core._hub_client import call_with_fallback_auth
127
127
  from lamindb_setup.core._hub_crud import (
128
- select_collaborator,
129
128
  update_instance,
130
129
  )
131
130
 
132
131
  if settings.instance.is_on_hub:
133
- # double check that user is an admin, otherwise will fail below
134
- # due to insufficient SQL permissions with cryptic error
135
- collaborator = call_with_fallback_auth(
136
- select_collaborator,
137
- instance_id=settings.instance._id,
138
- account_id=settings.user._uuid,
139
- fine_grained_access=settings.instance._fine_grained_access,
140
- )
141
- if collaborator is None or collaborator["role"] != "admin":
142
- raise SystemExit(
143
- "❌ Only admins can deploy migrations, please ensure that you're an"
144
- f" admin: https://lamin.ai/{settings.instance.slug}/settings"
145
- )
146
132
  # ensure we connect with the root user
147
133
  if "root" not in settings.instance.db:
148
134
  connect(use_root_db_user=True)
@@ -172,7 +172,8 @@ class _ModelHandler:
172
172
  self.table_name = model._meta.db_table
173
173
  self.included_modules = included_modules
174
174
  self.fields = self._get_fields_metadata(self.model)
175
- self.is_link_table = issubclass(model, IsLink)
175
+ self.is_auto_created = bool(model._meta.auto_created)
176
+ self.is_link_table = issubclass(model, IsLink) or self.is_auto_created
176
177
  self.name_field = model._name_field if hasattr(model, "_name_field") else None
177
178
  self.ontology_id_field = (
178
179
  model._ontology_id_field if hasattr(model, "_ontology_id_field") else None
@@ -183,6 +184,7 @@ class _ModelHandler:
183
184
  "fields": self.fields.copy(),
184
185
  "class_name": self.class_name,
185
186
  "table_name": self.table_name,
187
+ "is_auto_created": self.is_auto_created,
186
188
  "is_link_table": self.is_link_table,
187
189
  "name_field": self.name_field,
188
190
  "ontology_id_field": self.ontology_id_field,
@@ -249,13 +251,13 @@ class _ModelHandler:
249
251
  return related_fields
250
252
 
251
253
  def _get_field_metadata(self, model, field: Field):
252
- from lamindb.models import IsLink
254
+ from lamindb.models import IsLink, Registry
253
255
 
254
256
  internal_type = field.get_internal_type()
255
257
  model_name = field.model._meta.model_name
256
258
  relation_type = self._get_relation_type(model, field)
257
259
 
258
- schema_name = field.model.__get_module_name__()
260
+ schema_name = Registry.__get_module_name__(field.model)
259
261
 
260
262
  if field.related_model is None:
261
263
  related_model_name = None
@@ -265,7 +267,7 @@ class _ModelHandler:
265
267
  max_length = field.max_length
266
268
  else:
267
269
  related_model_name = field.related_model._meta.model_name
268
- related_schema_name = field.related_model.__get_module_name__()
270
+ related_schema_name = Registry.__get_module_name__(field.related_model)
269
271
  related_field_name = field.remote_field.name
270
272
  is_editable = False
271
273
  max_length = None
@@ -418,14 +420,10 @@ class _SchemaHandler:
418
420
  all_models = {module_name: {} for module_name in self.included_modules}
419
421
 
420
422
  # Iterate through all registered Django models
421
- for model in apps.get_models():
423
+ for model in apps.get_models(include_auto_created=True):
422
424
  # Check if model meets the criteria
423
- if (
424
- model.__class__ is Registry
425
- and model is not SQLRecord
426
- and not model._meta.abstract
427
- ):
428
- module_name = model.__get_module_name__()
425
+ if model is not SQLRecord and not model._meta.abstract:
426
+ module_name = Registry.__get_module_name__(model)
429
427
  # Only include if module is in our included list
430
428
  if module_name in self.included_modules:
431
429
  model_name = model._meta.model_name
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ from time import sleep
4
5
  from typing import TYPE_CHECKING
5
6
 
6
7
  from lamin_utils import logger
@@ -43,6 +44,14 @@ def load_user(email: str | None = None, handle: str | None = None) -> UserSettin
43
44
  return user_settings
44
45
 
45
46
 
47
+ def current_user_uid() -> str:
48
+ current_user_settings = current_user_settings_file()
49
+ if current_user_settings.exists():
50
+ return load_user_settings(current_user_settings).uid
51
+
52
+ return "00000000" # anonymous
53
+
54
+
46
55
  def login(
47
56
  user: str | None = None, *, api_key: str | None = None, **kwargs
48
57
  ) -> UserSettings:
@@ -51,15 +60,14 @@ def login(
51
60
 
52
61
  `login()` prompts for your API key unless you set it via the `LAMIN_API_KEY` environment variable or pass it as an argument.
53
62
 
54
- You can create your API key in your account settings on LaminHub (top right corner).
63
+ You can create your API key in your account settings on LaminHub at `lamin.ai/settings <https://lamin.ai/settings>`__.
64
+
65
+ Note that the preferred method is to use the CLI command `lamin login`: `docs.lamin.ai/cli#login <https://docs.lamin.ai/cli#login>`__.
55
66
 
56
67
  Args:
57
68
  user: User handle.
58
69
  api_key: API key.
59
70
 
60
- See Also:
61
- Login via the CLI command `lamin login`, see `here <https://docs.lamin.ai/cli#login>`__.
62
-
63
71
  Examples:
64
72
 
65
73
  Logging in the first time::
@@ -72,12 +80,15 @@ def login(
72
80
 
73
81
  ln.setup.login("myhandle") # pass your user handle
74
82
  """
83
+ from getpass import getpass
84
+
75
85
  if user is None:
76
86
  if api_key is None:
77
87
  if "LAMIN_API_KEY" in os.environ:
78
88
  api_key = os.environ["LAMIN_API_KEY"]
79
89
  else:
80
- api_key = input("Your API key: ")
90
+ print("Copy your API key. To create one: https://lamin.ai/settings")
91
+ api_key = getpass("Paste it: ")
81
92
  elif api_key is not None:
82
93
  raise ValueError("Please provide either 'user' or 'api_key', not both.")
83
94
 
@@ -90,6 +101,9 @@ def login(
90
101
  "the legacy API key is deprecated and will likely be removed in a future version"
91
102
  )
92
103
 
104
+ # do this here because load_user overwrites current_user_settings_file
105
+ previous_user_uid = current_user_uid()
106
+
93
107
  if api_key is None:
94
108
  if "@" in user: # type: ignore
95
109
  email, handle = user, None
@@ -144,8 +158,15 @@ def login(
144
158
  user_settings.api_key = api_key
145
159
  save_user_settings(user_settings)
146
160
 
147
- if settings._instance_exists and _check_instance_setup():
148
- register_user(user_settings)
161
+ if settings._instance_exists:
162
+ if (
163
+ isettings := settings.instance
164
+ ).is_on_hub and previous_user_uid != user_settings.uid:
165
+ logger.important_hint(
166
+ f"consider re-connecting to update permissions: lamin connect {isettings.slug}"
167
+ )
168
+ if _check_instance_setup():
169
+ register_user(user_settings)
149
170
 
150
171
  settings._user_settings = None
151
172
  # aws s3 credentials are scoped to the user
@@ -25,6 +25,8 @@ def silence_loggers():
25
25
  set_stream_logger(name="botocore.auth", level=logging.WARNING)
26
26
  set_stream_logger(name="botocore.endpoint", level=logging.WARNING)
27
27
  set_stream_logger(name="httpx", level=logging.WARNING)
28
+ set_stream_logger(name="httpcore", level=logging.WARNING)
29
+ set_stream_logger(name="hpack", level=logging.WARNING)
28
30
  try:
29
31
  import aiobotocore
30
32
 
@@ -60,6 +60,7 @@ class AWSOptionsManager:
60
60
  from aiobotocore.session import AioSession
61
61
  from s3fs import S3FileSystem
62
62
 
63
+ anon_env = os.getenv("LAMIN_S3_ANON") == "true"
63
64
  # this is cached so will be resued with the connection initialized
64
65
  # these options are set for paths in _path_inject_options
65
66
  # here we set the same options to cache the filesystem
@@ -68,19 +69,28 @@ class AWSOptionsManager:
68
69
  use_listings_cache=True,
69
70
  version_aware=False,
70
71
  config_kwargs={"max_pool_connections": 64},
72
+ anon=anon_env,
71
73
  )
72
74
 
73
75
  self._suppress_aiobotocore_traceback_logging()
74
76
 
75
- try:
76
- fs.connect()
77
- self.anon: bool = fs.session._credentials is None
78
- except Exception as e:
77
+ if anon_env:
78
+ self.anon: bool = True
79
79
  logger.warning(
80
- f"There is a problem with your default AWS Credentials: {e}\n"
81
- "`anon` mode will be used for all non-managed buckets."
80
+ "`anon` mode will be used for all non-managed buckets "
81
+ "because the environment variable LAMIN_S3_ANON was set to 'true'"
82
82
  )
83
- self.anon = True
83
+ else:
84
+ try:
85
+ fs.connect()
86
+ self.anon = fs.session._credentials is None
87
+ except Exception as e:
88
+ logger.warning(
89
+ f"There is a problem with your default AWS Credentials: {e}\n"
90
+ "`anon` mode will be used for all non-managed buckets"
91
+ )
92
+ self.anon = True
93
+
84
94
  self.anon_public: bool | None = None
85
95
  if not self.anon:
86
96
  try:
@@ -5,6 +5,7 @@
5
5
 
6
6
  init_local_sqlite
7
7
  connect_local_sqlite
8
+ connect_remote_sqlite
8
9
  upload_sqlite_clone
9
10
  """
10
11
 
@@ -13,7 +14,6 @@ import os
13
14
  import shutil
14
15
  from pathlib import Path
15
16
 
16
- from lamindb_setup.core._settings_instance import InstanceSettings
17
17
  from lamindb_setup.core._settings_load import load_instance_settings
18
18
  from lamindb_setup.core._settings_store import instance_settings_file
19
19
  from lamindb_setup.core.django import reset_django
@@ -11,8 +11,7 @@ import httpx
11
11
  from httpx_retries import Retry, RetryTransport
12
12
  from lamin_utils import logger
13
13
  from pydantic_settings import BaseSettings
14
- from supabase import Client, create_client # type: ignore
15
- from supabase.lib.client_options import ClientOptions
14
+ from supabase import Client, ClientOptions, create_client
16
15
 
17
16
  from ._settings_save import save_user_settings
18
17
 
@@ -383,7 +383,8 @@ def _init_instance_hub(
383
383
  ) -> None:
384
384
  from ._settings import settings
385
385
 
386
- account_id = settings.user._uuid if account_id is None else account_id
386
+ created_by_id = settings.user._uuid.hex if account_id is None else account_id.hex # type: ignore
387
+ owner_account_id = os.getenv("LAMINDB_ACCOUNT_ID_INIT", created_by_id)
387
388
 
388
389
  try:
389
390
  lamindb_version = metadata.version("lamindb")
@@ -391,13 +392,13 @@ def _init_instance_hub(
391
392
  lamindb_version = None
392
393
  fields = {
393
394
  "id": isettings._id.hex,
394
- "account_id": account_id.hex, # type: ignore
395
+ "account_id": owner_account_id,
395
396
  "name": isettings.name,
396
397
  "lnid": isettings.uid,
397
398
  "schema_str": isettings._schema_str,
398
399
  "lamindb_version": lamindb_version,
399
400
  "public": False,
400
- "created_by_id": account_id.hex, # type: ignore
401
+ "created_by_id": created_by_id,
401
402
  }
402
403
  if isettings.dialect != "sqlite":
403
404
  db_dsn = LaminDsnModel(db=isettings.db)
@@ -407,7 +408,7 @@ def _init_instance_hub(
407
408
  "db_port": db_dsn.db.port,
408
409
  "db_database": db_dsn.db.database,
409
410
  }
410
- fields.update(db_fields)
411
+ fields.update(db_fields) # type: ignore
411
412
  slug = isettings.slug
412
413
  # I'd like the following to be an upsert, but this seems to violate RLS
413
414
  # Similarly, if we don't specify `returning="minimal"`, we'll violate RLS
@@ -415,7 +416,9 @@ def _init_instance_hub(
415
416
  # as then init_instance is no longer idempotent
416
417
  try:
417
418
  client.table("instance").insert(fields, returning="minimal").execute()
418
- except APIError:
419
+ except APIError as e:
420
+ if "new row violates row-level security policy" in str(e):
421
+ raise e
419
422
  logger.warning(f"instance already existed at: https://lamin.ai/{slug}")
420
423
  return None
421
424
  if isettings.dialect != "sqlite" and isettings.is_remote:
@@ -713,7 +716,7 @@ def get_lamin_site_base_url():
713
716
 
714
717
 
715
718
  def sign_up_local_hub(email) -> str | tuple[str, str, str]:
716
- # raises gotrue.errors.AuthApiError: User already registered
719
+ # raises AuthApiError: User already registered
717
720
  password = base62(40) # generate new password
718
721
  sign_up_kwargs = {"email": email, "password": password}
719
722
  client = connect_hub()
@@ -37,7 +37,6 @@ def private_django_api(reverse=False):
37
37
  "MultipleObjectsReturned",
38
38
  "add_to_class",
39
39
  "adelete",
40
- "refresh_from_db",
41
40
  "asave",
42
41
  "clean",
43
42
  "clean_fields",