lamindb_setup 1.16.0__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.16.0" # 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)
@@ -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
@@ -59,15 +60,14 @@ def login(
59
60
 
60
61
  `login()` prompts for your API key unless you set it via the `LAMIN_API_KEY` environment variable or pass it as an argument.
61
62
 
62
- 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>`__.
63
66
 
64
67
  Args:
65
68
  user: User handle.
66
69
  api_key: API key.
67
70
 
68
- See Also:
69
- Login via the CLI command `lamin login`, see `here <https://docs.lamin.ai/cli#login>`__.
70
-
71
71
  Examples:
72
72
 
73
73
  Logging in the first time::
@@ -80,12 +80,15 @@ def login(
80
80
 
81
81
  ln.setup.login("myhandle") # pass your user handle
82
82
  """
83
+ from getpass import getpass
84
+
83
85
  if user is None:
84
86
  if api_key is None:
85
87
  if "LAMIN_API_KEY" in os.environ:
86
88
  api_key = os.environ["LAMIN_API_KEY"]
87
89
  else:
88
- 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: ")
89
92
  elif api_key is not None:
90
93
  raise ValueError("Please provide either 'user' or 'api_key', not both.")
91
94
 
@@ -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:
@@ -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()
@@ -46,6 +46,12 @@ def _process_cache_path(cache_path: UPathStr | None) -> UPath | None:
46
46
  return cache_dir
47
47
 
48
48
 
49
+ # returned by settings.branch for none/none instance
50
+ class MainBranchMock:
51
+ id = 1
52
+ name = "main"
53
+
54
+
49
55
  class SetupSettings:
50
56
  """Setup settings."""
51
57
 
@@ -140,6 +146,10 @@ class SetupSettings:
140
146
  # and we never need a DB request
141
147
  def branch(self) -> Branch:
142
148
  """Default branch."""
149
+ # this is needed for .filter() with non-default connections
150
+ if not self._instance_exists:
151
+ return MainBranchMock()
152
+
143
153
  if self._branch is None:
144
154
  from lamindb import Branch
145
155
 
@@ -222,10 +232,9 @@ class SetupSettings:
222
232
  If `True`, the current instance is connected, meaning that the db and other settings
223
233
  are properly configured for use.
224
234
  """
225
- if self._instance_exists:
226
- return self.instance.slug != "none/none"
227
- else:
228
- return False
235
+ from . import django
236
+
237
+ return self._instance_exists and django.IS_SETUP
229
238
 
230
239
  @property
231
240
  def private_django_api(self) -> bool:
@@ -284,12 +293,7 @@ class SetupSettings:
284
293
 
285
294
  @property
286
295
  def _instance_exists(self):
287
- try:
288
- self.instance # noqa
289
- return True
290
- # this is implicit logic that catches if no instance is loaded
291
- except CurrentInstanceNotConfigured:
292
- return False
296
+ return self.instance.slug != "none/none"
293
297
 
294
298
  @property
295
299
  def cache_dir(self) -> UPath:
@@ -125,10 +125,11 @@ class InstanceSettings:
125
125
  if self._local_storage is not None:
126
126
  value_local = self.local_storage
127
127
  representation += f"\n - local storage: {value_local.root_as_str} ({value_local.region})"
128
- representation += (
129
- f"\n - cloud storage: {value.root_as_str} ({value.region})"
130
- )
131
- else:
128
+ if value is not None:
129
+ representation += (
130
+ f"\n - cloud storage: {value.root_as_str} ({value.region})"
131
+ )
132
+ elif value is not None:
132
133
  representation += (
133
134
  f"\n - storage: {value.root_as_str} ({value.region})"
134
135
  )
@@ -513,17 +514,36 @@ class InstanceSettings:
513
514
 
514
515
  @property
515
516
  def dialect(self) -> Literal["sqlite", "postgresql"]:
516
- """SQL dialect."""
517
+ """SQL dialect.
518
+
519
+ Equivalent to :attr:`vendor`.
520
+
521
+ "vendor" is the Django terminology for the type of database. "dialect" is the SQLAlchemy terminology.
522
+ """
517
523
  if self._db is None or self._db.startswith("sqlite://"):
518
524
  return "sqlite"
519
525
  else:
520
526
  assert self._db.startswith("postgresql"), f"Unexpected DB value: {self._db}"
521
527
  return "postgresql"
522
528
 
529
+ @property
530
+ def vendor(self) -> Literal["sqlite", "postgresql"]:
531
+ """Database vendor.
532
+
533
+ Equivalent to :attr:`dialect`.
534
+
535
+ "vendor" is the Django terminology for the type of database. "dialect" is the SQLAlchemy terminology.
536
+ """
537
+ return self.dialect
538
+
523
539
  @property
524
540
  def _is_cloud_sqlite(self) -> bool:
525
541
  """Is this a cloud instance with sqlite db."""
526
- return self.dialect == "sqlite" and self.storage.type_is_cloud
542
+ return (
543
+ self.dialect == "sqlite"
544
+ and self.storage is not None
545
+ and self.storage.type_is_cloud
546
+ )
527
547
 
528
548
  @property
529
549
  def _cloud_sqlite_locker(self):
@@ -543,6 +563,8 @@ class InstanceSettings:
543
563
  @property
544
564
  def is_remote(self) -> bool:
545
565
  """Boolean indicating if an instance has no local component."""
566
+ if self.storage is None and self.db == "sqlite:///:memory:":
567
+ return False
546
568
  return check_is_instance_remote(self.storage.root_as_str, self.db)
547
569
 
548
570
  @property
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ from importlib.util import find_spec
4
5
  from pathlib import Path
5
6
  from typing import TYPE_CHECKING
6
7
  from uuid import UUID, uuid4
@@ -46,19 +47,36 @@ def load_cache_path_from_settings(storage_settings: Path | None = None) -> Path
46
47
  return None
47
48
 
48
49
 
50
+ def find_module_candidates():
51
+ """Find all local packages that depend on lamindb."""
52
+ candidates = ["bionty", "wetlab"]
53
+ return [c for c in candidates if find_spec(c) is not None]
54
+
55
+
49
56
  def load_instance_settings(instance_settings_file: Path | None = None):
50
57
  if instance_settings_file is None:
51
- instance_settings_file = current_instance_settings_file()
52
- if not instance_settings_file.exists():
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
53
73
  raise CurrentInstanceNotConfigured
54
74
  try:
55
- settings_store = InstanceSettingsStore(_env_file=instance_settings_file)
75
+ settings_store = InstanceSettingsStore(_env_file=isettings_file)
56
76
  except (ValidationError, TypeError) as error:
57
- with open(instance_settings_file) as f:
58
- content = f.read()
59
77
  raise SettingsEnvFileOutdated(
60
- f"\n\n{error}\n\nYour instance settings file with\n\n{content}\nis invalid"
61
- f" (likely outdated), see validation error. Please delete {instance_settings_file} &"
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} &"
62
80
  " reload (remote) or re-initialize (local) the instance with the same name & storage location."
63
81
  ) from error
64
82
  isettings = setup_instance_from_store(settings_store)
@@ -5,13 +5,15 @@ import builtins
5
5
  import os
6
6
  import sys
7
7
  import importlib as il
8
+ import gzip
8
9
  import jwt
9
10
  import time
10
11
  import threading
11
12
  from pathlib import Path
13
+ import shutil
12
14
  from packaging import version
13
15
  from ._settings_instance import InstanceSettings, is_local_db_url
14
-
16
+ from ..errors import CurrentInstanceNotConfigured
15
17
  from lamin_utils import logger
16
18
 
17
19
 
@@ -21,6 +23,24 @@ IS_MIGRATING = False
21
23
  CONN_MAX_AGE = 299
22
24
 
23
25
 
26
+ def get_connection(connection_name: str):
27
+ from django.db import connections
28
+
29
+ return connections[connection_name]
30
+
31
+
32
+ def error_no_instance_wrapper(execute, sql, params, many, context):
33
+ connection = context["connection"]
34
+
35
+ if (
36
+ connection.vendor == "sqlite"
37
+ and connection.settings_dict.get("NAME") == ":memory:"
38
+ ):
39
+ raise CurrentInstanceNotConfigured
40
+
41
+ return execute(sql, params, many, context)
42
+
43
+
24
44
  # db token that refreshes on access if needed
25
45
  class DBToken:
26
46
  def __init__(
@@ -64,11 +84,6 @@ class DBTokenManager:
64
84
 
65
85
  self.tokens: dict[str, DBToken] = {}
66
86
 
67
- def get_connection(self, connection_name: str):
68
- from django.db import connections
69
-
70
- return connections[connection_name]
71
-
72
87
  def set(self, token: DBToken, connection_name: str = "default"):
73
88
  if connection_name in self.tokens:
74
89
  return
@@ -77,11 +92,7 @@ class DBTokenManager:
77
92
  from django.db.backends.signals import connection_created
78
93
 
79
94
  def set_token_wrapper(execute, sql, params, many, context):
80
- not_in_atomic_block = (
81
- context is None
82
- or "connection" not in context
83
- or not context["connection"].in_atomic_block
84
- )
95
+ not_in_atomic_block = not context["connection"].in_atomic_block
85
96
  # ignore atomic blocks
86
97
  if not_in_atomic_block:
87
98
  sql = token.token_query + sql
@@ -98,7 +109,7 @@ class DBTokenManager:
98
109
  result.nextset()
99
110
  return result
100
111
 
101
- self.get_connection(connection_name).execute_wrappers.append(set_token_wrapper)
112
+ get_connection(connection_name).execute_wrappers.append(set_token_wrapper)
102
113
 
103
114
  def connection_callback(sender, connection, **kwargs):
104
115
  if (
@@ -124,7 +135,7 @@ class DBTokenManager:
124
135
  if connection_name in self.tokens:
125
136
  # here we don't use the connection from the closure
126
137
  # because Atomic is a single class to manage transactions for all connections
127
- connection = self.get_connection(connection_name)
138
+ connection = get_connection(connection_name)
128
139
  if len(connection.atomic_blocks) == 1:
129
140
  token = self.tokens[connection_name]
130
141
  # use raw psycopg2 connection here
@@ -142,7 +153,7 @@ class DBTokenManager:
142
153
 
143
154
  from django.db.backends.signals import connection_created
144
155
 
145
- connection = self.get_connection(connection_name)
156
+ connection = get_connection(connection_name)
146
157
 
147
158
  connection.execute_wrappers = [
148
159
  w
@@ -291,6 +302,9 @@ def setup_django(
291
302
  django.db.connections._connections = threading.local()
292
303
  logger.debug("django.db.connections._connections has been patched")
293
304
 
305
+ # error if trying to query with the default connection without setting up an instance
306
+ get_connection("default").execute_wrappers.insert(0, error_no_instance_wrapper)
307
+
294
308
  if isettings._fine_grained_access and isettings._db_permissions == "jwt":
295
309
  db_token = DBToken(isettings)
296
310
  db_token_manager.set(db_token) # sets for the default connection
@@ -311,10 +325,24 @@ def setup_django(
311
325
  call_command("migrate", app_name, app_number, verbosity=2)
312
326
  isettings._update_cloud_sqlite_file(unlock_cloud_sqlite=False)
313
327
  elif init:
314
- global IS_MIGRATING
315
- IS_MIGRATING = True
316
- call_command("migrate", verbosity=0)
317
- IS_MIGRATING = False
328
+ modules_beyond_bionty = isettings.modules.copy()
329
+ compressed_sqlite_path = Path(__file__).parent / "lamin.db.gz"
330
+ if "bionty" in modules_beyond_bionty:
331
+ modules_beyond_bionty.remove("bionty")
332
+ if (
333
+ isettings.dialect == "postgresql"
334
+ or os.getenv("LAMINDB_INIT_FROM_SCRATCH", "false") == "true"
335
+ or len(modules_beyond_bionty) > 0
336
+ or not compressed_sqlite_path.exists()
337
+ ):
338
+ global IS_MIGRATING
339
+ IS_MIGRATING = True
340
+ call_command("migrate", verbosity=0)
341
+ IS_MIGRATING = False
342
+ else:
343
+ with gzip.open(compressed_sqlite_path, "rb") as f_in:
344
+ with open(isettings._sqlite_file_local, "wb") as f_out:
345
+ shutil.copyfileobj(f_in, f_out)
318
346
 
319
347
  global IS_SETUP
320
348
  IS_SETUP = True
Binary file
@@ -1005,13 +1005,22 @@ def check_storage_is_empty(
1005
1005
  objects = [o for o in objects if "/.lamindb/_exclusion/" not in o]
1006
1006
  n_files = len(objects)
1007
1007
  n_diff = n_files - n_offset_objects
1008
- ask_for_deletion = (
1009
- "delete them prior to deleting the storage location"
1010
- if raise_error
1011
- else "consider deleting them"
1012
- )
1013
- message = f"'{directory_string}' contains {n_diff} objects - {ask_for_deletion}"
1014
1008
  if n_diff > 0:
1009
+ ask_for_deletion = (
1010
+ "delete them prior to deleting the storage location"
1011
+ if raise_error
1012
+ else "consider deleting them"
1013
+ )
1014
+ message = f"'{directory_string}' contains {n_diff} objects:\n"
1015
+ message += "\n".join(
1016
+ [
1017
+ o
1018
+ for o in objects
1019
+ if not o.endswith(".lamindb/storage_uid.txt")
1020
+ and not (account_for_sqlite_file and o.endswith(".lamindb/lamin.db"))
1021
+ ]
1022
+ )
1023
+ message += f"\n{ask_for_deletion}"
1015
1024
  if raise_error:
1016
1025
  raise StorageNotEmpty(message) from None
1017
1026
  else:
lamindb_setup/errors.py CHANGED
@@ -1,7 +1,6 @@
1
1
  """Errors.
2
2
 
3
3
  .. autoexception:: CurrentInstanceNotConfigured
4
- .. autoexception:: InstanceNotSetupError
5
4
  .. autoexception:: ModuleWasntConfigured
6
5
  .. autoexception:: StorageAlreadyManaged
7
6
  .. autoexception:: StorageNotEmpty
@@ -25,17 +24,6 @@ class DefaultMessageException(Exception):
25
24
  super().__init__(message)
26
25
 
27
26
 
28
- # TODO: remove this exception sooner or later because we don't have a need for it anymore
29
- class InstanceNotSetupError(DefaultMessageException):
30
- default_message = """\
31
- To use lamindb, you need to connect to an instance.
32
-
33
- Connect to an instance: `ln.connect()`. Init an instance: `ln.setup.init()`.
34
-
35
- If you used the CLI to set up lamindb in a notebook, restart the Python session.
36
- """
37
-
38
-
39
27
  class CurrentInstanceNotConfigured(DefaultMessageException):
40
28
  default_message = """\
41
29
  No instance is connected! Call
lamindb_setup/io.py CHANGED
@@ -14,7 +14,7 @@ from django.db import models, transaction
14
14
  from rich.progress import Progress
15
15
 
16
16
  if TYPE_CHECKING:
17
- from collections.abc import Iterable, Sequence
17
+ from collections.abc import Iterable
18
18
  from typing import Literal
19
19
 
20
20
 
@@ -22,6 +22,9 @@ def _get_registries(module_name: str) -> list[str]:
22
22
  """Get registry class names from a module."""
23
23
  schema_module = import_module(module_name)
24
24
 
25
+ # Ensure that models are loaded; we've observed empty exports otherwise
26
+ from django.db import models
27
+
25
28
  return [
26
29
  name
27
30
  for name in dir(schema_module.models)
@@ -142,9 +145,9 @@ def _export_full_table(
142
145
 
143
146
 
144
147
  def export_db(
145
- module_names: Sequence[str] | None = None,
148
+ module_names: Iterable[str] | None = None,
146
149
  *,
147
- output_dir: str | Path = "./lamindb_export/",
150
+ output_dir: str | Path | None = None,
148
151
  max_workers: int = 8,
149
152
  chunk_size: int = 500_000,
150
153
  ) -> None:
@@ -159,6 +162,11 @@ def export_db(
159
162
  max_workers: Number of parallel processes.
160
163
  chunk_size: Number of rows per chunk for large tables.
161
164
  """
165
+ import lamindb_setup as ln_setup
166
+
167
+ if output_dir is None:
168
+ output_dir = f"./{ln_setup.settings.instance.name}_export/"
169
+
162
170
  directory = Path(output_dir)
163
171
  directory.mkdir(parents=True, exist_ok=True)
164
172
 
@@ -332,12 +340,15 @@ def import_db(
332
340
  Temporarily disables FK constraints to allow insertion in arbitrary order.
333
341
  Requires superuser/RDS admin privileges for postgres databases.
334
342
 
343
+ Note: When running in a subprocess, add a short delay or explicit connection close after `import_db()`
344
+ to ensure all SQLite writes are flushed to disk before process termination.
345
+
335
346
  Args:
336
347
  input_dir: Directory containing parquet files to import.
337
348
  module_names: Module names to import (e.g., ["lamindb", "bionty", "wetlab"]).
338
349
  if_exists: How to behave if table exists: 'fail', 'replace', or 'append'.
339
- If set to 'replace', existing data is deleted and new data is imported. PKs and indices are not guaranteed to be preserved which can lead to write errors.
340
- If set to 'append', new data is added to existing data without clearing the table. PKs and indices are preserved but database size will greatly increase.
350
+ If set to 'replace', existing data is deleted and new data is imported. All PKs and indices are not guaranteed to be preserved which can lead to write errors.
351
+ If set to 'append', new data is added to existing data without clearing the table. All PKs and indices are preserved allowing write operations but database size will greatly increase.
341
352
  If set to 'fail', raises an error if the table contains any data.
342
353
  """
343
354
  from django.db import connection
@@ -1,10 +1,11 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: lamindb_setup
3
- Version: 1.16.0
3
+ Version: 1.17.0
4
4
  Summary: Setup & configure LaminDB.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Requires-Python: >=3.10
7
7
  Description-Content-Type: text/markdown
8
+ License-File: LICENSE
8
9
  Requires-Dist: lamin_utils>=0.3.3
9
10
  Requires-Dist: django>=5.2,<5.3
10
11
  Requires-Dist: dj_database_url>=1.3.0,<3.0.0
@@ -15,9 +16,7 @@ Requires-Dist: httpx_retries<1.0.0
15
16
  Requires-Dist: requests
16
17
  Requires-Dist: universal_pathlib==0.2.6
17
18
  Requires-Dist: botocore<2.0.0
18
- Requires-Dist: supabase>=2.8.1,<=2.16.0
19
- Requires-Dist: gotrue<=2.12.0
20
- Requires-Dist: storage3!=0.11.2; python_version < '3.11'
19
+ Requires-Dist: supabase>=2.20.0,<=2.24.0
21
20
  Requires-Dist: pyjwt<3.0.0
22
21
  Requires-Dist: psutil
23
22
  Requires-Dist: packaging
@@ -1,50 +1,51 @@
1
- lamindb_setup/__init__.py,sha256=zAgDC8o3cyH4eXcM60mbulFPECs5XZbqFRxLfGpgpc8,3215
1
+ lamindb_setup/__init__.py,sha256=MW5TlycTeavLeMWfllbDgMo2z4r7zlNHHvMLEaJRnGE,3270
2
2
  lamindb_setup/_cache.py,sha256=pGvDNVHGx4HWr_6w5ajqEJOdysmaGc6F221qFnXkT-k,2747
3
3
  lamindb_setup/_check.py,sha256=28PcG8Kp6OpjSLSi1r2boL2Ryeh6xkaCL87HFbjs6GA,129
4
- lamindb_setup/_check_setup.py,sha256=ToKMxsUq8dQBQh8baOrNVlSb1iC8h4zTg5dV8wMu0W4,6760
5
- lamindb_setup/_connect_instance.py,sha256=3dsaZ7LGzJYtOWDbi2RkiVJIJqdxy43suNjQ-6C96_U,17788
6
- lamindb_setup/_delete.py,sha256=KS3r-xGFuDmAbzPUy-9JR-YnPShYdaHjDRQrAmXQ0qM,5863
7
- lamindb_setup/_disconnect.py,sha256=FT8EpCm5XXDdhDH7QtAnkO3KPatq2HqT9VXGNjgJDbk,1232
4
+ lamindb_setup/_check_setup.py,sha256=zPQho12dctJYjnZxpumq2r7XRvXwrNyWi5-UMRPSeuE,4297
5
+ lamindb_setup/_connect_instance.py,sha256=AVJ5wDGsvPO1HAWw2KIz0R2hF0c9DMLJ_2kFsXJfwd4,17156
6
+ lamindb_setup/_delete.py,sha256=ZMAik35NNcj4kDfOISDfzpicCi4j8gwmWAor0hEB-Jk,6123
7
+ lamindb_setup/_disconnect.py,sha256=B0K0yTIIyhwc3MzDFL_-cKutm-_DHsiJSTrkp1MMyXA,1470
8
8
  lamindb_setup/_django.py,sha256=uIQflpkp8l3axyPaKURlk3kacgpElVP5KOKmFxYSMGk,1454
9
9
  lamindb_setup/_entry_points.py,sha256=sKwXPX9xjOotoAjvgkU5LBwjjHLWVkh0ZGdiSsrch9k,522
10
- lamindb_setup/_init_instance.py,sha256=zNXmZPUHYda1CfLGtsvo4gNhHprK9QVPfffUIfBlTxE,14938
11
- lamindb_setup/_migrate.py,sha256=SN8uphuQX-8XShH5odLyzV8-eyXATDxB5hWoxwxmgBU,11264
10
+ lamindb_setup/_init_instance.py,sha256=EBdv0ga_nnqHU-m9fN7EkVg7YEuKOLxR8N87Ww2qEDg,14877
11
+ lamindb_setup/_migrate.py,sha256=B7dRb4hIkuHlRxmI25N7xkdQve-fNZDZoq4GsP3TtH0,10512
12
12
  lamindb_setup/_register_instance.py,sha256=RdUZxZWHLdbvdNZWpF8e0UWROb_T0cStWbzc5yUw34I,1047
13
13
  lamindb_setup/_schema.py,sha256=b3uzhhWpV5mQtDwhMINc2MabGCnGLESy51ito3yl6Wc,679
14
14
  lamindb_setup/_schema_metadata.py,sha256=Whs-e4ZMnA1niZ2l5Eu8il-33IxI4Hr5ylGEgPxx8wk,15628
15
15
  lamindb_setup/_set_managed_storage.py,sha256=xQe5DXCRiQ5VseAjVC2Bki0wB0n0tSTchvVKSx9I6eo,3094
16
- lamindb_setup/_setup_user.py,sha256=cjQ-Md-FkP04PnBxocbHW6wCsZsNtD2T2NB52vAOnHI,6730
17
- lamindb_setup/_silence_loggers.py,sha256=AKF_YcHvX32eGXdsYK8MJlxEaZ-Uo2f6QDRzjKFCtws,1568
18
- lamindb_setup/errors.py,sha256=lccF3X3M2mcbHVG_0HxfuJRFFpUE-42paccIxFOfefQ,1958
19
- lamindb_setup/io.py,sha256=9s4Itt4rrHzsUATY79r4nhGp9zVAm-9uBhiQgg60l6U,16708
16
+ lamindb_setup/_setup_user.py,sha256=9CwKUru2jza1vXu4A5V-q-oXnbIXwR_YwjKbVnETK7Q,6931
17
+ lamindb_setup/_silence_loggers.py,sha256=oQnvnvieqxARQQMwQkR82EJVk1TaM-Bo9o0UCOsCcXY,1697
18
+ lamindb_setup/errors.py,sha256=hELv-tJSjyPDqT9pZ_-tmg6q_wXDtAgn8m1fIvFeMtg,1528
19
+ lamindb_setup/io.py,sha256=stTE6VHCIpLK1ueRuWEJGh35wo9Qd1NcNN1TnLEbK-U,17156
20
20
  lamindb_setup/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
21
  lamindb_setup/types.py,sha256=fuQxZJnrGYe7a_Ju9n1RqO-HhkOAr1l1xjpAg9dmBu8,605
22
22
  lamindb_setup/core/__init__.py,sha256=adZtacDwG2C0tgx-ypp9yOAqw9qaR-IRWkgLurKpXVE,668
23
- lamindb_setup/core/_aws_options.py,sha256=9kQ5BB-cuJQrlJRGNqMRe1m48dP67xMbefOJP2c9OQw,9674
23
+ lamindb_setup/core/_aws_options.py,sha256=5XbcPtRU_oVrfbuG_0kngyYfxswLLnRS3nKO4XEWepU,10054
24
24
  lamindb_setup/core/_aws_storage.py,sha256=QEtV-riQrwfivcwqHnXBbkJ-9YyNEXL4fLoCmOHZ1BI,2003
25
25
  lamindb_setup/core/_clone.py,sha256=oLTMItGxRQB1THSDP-RG2eH1qPTVcuZ7_-eElttJ518,6451
26
26
  lamindb_setup/core/_deprecated.py,sha256=M3vpM4fZPOncxY2qsXQAPeaEph28xWdv7tYaueaUyAA,2554
27
27
  lamindb_setup/core/_docs.py,sha256=3k-YY-oVaJd_9UIY-LfBg_u8raKOCNfkZQPA73KsUhs,276
28
- lamindb_setup/core/_hub_client.py,sha256=vem145S5ppRPcWob7iclGhos8k-BfwJi9AI-l5PteDs,10481
29
- lamindb_setup/core/_hub_core.py,sha256=GAQK5XkHROIuqA-H8sOQZVlxvN4QIH_cmHY0TENnq2U,29090
28
+ lamindb_setup/core/_hub_client.py,sha256=113SKT3Ki4KxyKqpYOwIB8mEG0ouy9oebLJoh_xGJZY,10426
29
+ lamindb_setup/core/_hub_core.py,sha256=29iPGnmV1hzYIMrErgwJoHwTQ5RHNJ6-icDM81aC4ac,29255
30
30
  lamindb_setup/core/_hub_crud.py,sha256=j6516H82kLjFUNPqFGUINbDw9YbofMgjxadGzYb0OS4,6362
31
31
  lamindb_setup/core/_hub_utils.py,sha256=6dyDGyzYFgVfR_lE3VN3CP1jGp98gxPtr-T91PAP05U,2687
32
32
  lamindb_setup/core/_private_django_api.py,sha256=Z9uGL4CK0OX58rc8R_qarg9rIBp1DgjsjfP9Vj2vJHI,2629
33
- lamindb_setup/core/_settings.py,sha256=QbTrSkkdx0u685NJ4neNtWzhdHoaGMKcIvrfFnctTQ4,15450
34
- lamindb_setup/core/_settings_instance.py,sha256=eDkueLK5JZOGFhZRbGa-OffS9iBFlxMp47vF_MfmCYI,24301
35
- lamindb_setup/core/_settings_load.py,sha256=NQDOln8e3qyGphk8ucU7mm3HVkCv4QV4rDZro3TIwfo,5183
33
+ lamindb_setup/core/_settings.py,sha256=LTeh2jsM01GHW2ZTrcc6rXNz6qIUT0VPEzpE9I8tg3Q,15497
34
+ lamindb_setup/core/_settings_instance.py,sha256=EkKcT5TcGLIiMQdiTNCrf8OPp0wmj1ZmY_9kL7ovhZc,24994
35
+ lamindb_setup/core/_settings_load.py,sha256=D6r0fiqJznHhTN_apGlrKfYzl0g21shIaV4z4zEr7Z8,5780
36
36
  lamindb_setup/core/_settings_save.py,sha256=96mWdYLyfvbnG_ok_vK4x7jm-rtqcWCD1OHEt2QSAms,3328
37
37
  lamindb_setup/core/_settings_storage.py,sha256=22EBagIr5qOZr9pqVkJsTcQtgE14en-Wh0y9rgF4FEQ,15677
38
38
  lamindb_setup/core/_settings_store.py,sha256=auZssUBb6qE5oSqdGiHhqI2B46qSpegX89VwObPQksk,2601
39
39
  lamindb_setup/core/_settings_user.py,sha256=gFfyMf-738onbh1Mf4wsmLlenQJPtjQfpUgKnOlqc2o,1453
40
40
  lamindb_setup/core/_setup_bionty_sources.py,sha256=ox3X-SHiHa2lNPSWjwZhINypbLacX6kGwH6hVVrSFZc,1505
41
41
  lamindb_setup/core/cloud_sqlite_locker.py,sha256=H_CTUCjURFXwD1cCtV_Jn0_60iztZTkaesLLXIBgIxc,7204
42
- lamindb_setup/core/django.py,sha256=aBdIN07ZCD8PGT04sjF1rruwlppx0cEobBnjk2Due70,12525
42
+ lamindb_setup/core/django.py,sha256=JTlGZ2I9EGTr49NjLMTHY1FfjkOoqcoYNgMQitQRGyQ,13640
43
43
  lamindb_setup/core/exceptions.py,sha256=qjMzqy_uzPA7mCOdnoWnS_fdA6OWbdZGftz-YYplrY0,84
44
44
  lamindb_setup/core/hashing.py,sha256=Y8Uc5uSGTfU6L2R_gb5w8DdHhGRog7RnkK-e9FEMjPY,3680
45
+ lamindb_setup/core/lamin.db.gz,sha256=cSWQ_ak6QN-SMyFA4x6zv-e2rNXr1IOrhDU5AfVAADc,81394
45
46
  lamindb_setup/core/types.py,sha256=T7NwspfRHgIIpYsXDcApks8jkOlGeGRW-YbVLB7jNIo,67
46
- lamindb_setup/core/upath.py,sha256=_xs6CgqQezOe6h8oQURjpOl1WT_1ctROzH3yzesVceE,36188
47
- lamindb_setup-1.16.0.dist-info/LICENSE,sha256=UOZ1F5fFDe3XXvG4oNnkL1-Ecun7zpHzRxjp-XsMeAo,11324
48
- lamindb_setup-1.16.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
49
- lamindb_setup-1.16.0.dist-info/METADATA,sha256=mkCWug_dpZRqU6Y0ajukECuIC8kezhh4bTYAIuDLAao,1830
50
- lamindb_setup-1.16.0.dist-info/RECORD,,
47
+ lamindb_setup/core/upath.py,sha256=zlPZcDRHDYT7OcoOiX_w9koAzfBr9q8hB66OPwhTpBo,36504
48
+ lamindb_setup-1.17.0.dist-info/licenses/LICENSE,sha256=UOZ1F5fFDe3XXvG4oNnkL1-Ecun7zpHzRxjp-XsMeAo,11324
49
+ lamindb_setup-1.17.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
50
+ lamindb_setup-1.17.0.dist-info/METADATA,sha256=Q8iSdBECnZFm2PKkraif0zjcwm5CqSem3NW0UdCPBHo,1766
51
+ lamindb_setup-1.17.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: flit 3.10.1
2
+ Generator: flit 3.12.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any