lamindb_setup 0.70.0__py2.py3-none-any.whl → 0.71.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. lamindb_setup/__init__.py +15 -15
  2. lamindb_setup/_add_remote_storage.py +22 -33
  3. lamindb_setup/_cache.py +4 -1
  4. lamindb_setup/_check.py +3 -0
  5. lamindb_setup/_check_setup.py +13 -7
  6. lamindb_setup/_close.py +2 -0
  7. lamindb_setup/_connect_instance.py +33 -26
  8. lamindb_setup/_delete.py +52 -19
  9. lamindb_setup/_django.py +4 -1
  10. lamindb_setup/_exportdb.py +4 -2
  11. lamindb_setup/_importdb.py +5 -1
  12. lamindb_setup/_init_instance.py +57 -45
  13. lamindb_setup/_migrate.py +16 -13
  14. lamindb_setup/_register_instance.py +10 -3
  15. lamindb_setup/_schema.py +6 -3
  16. lamindb_setup/_setup_user.py +7 -7
  17. lamindb_setup/_silence_loggers.py +4 -2
  18. lamindb_setup/core/__init__.py +4 -3
  19. lamindb_setup/core/_aws_storage.py +3 -0
  20. lamindb_setup/core/_deprecated.py +2 -7
  21. lamindb_setup/core/_docs.py +2 -0
  22. lamindb_setup/core/_hub_client.py +12 -10
  23. lamindb_setup/core/_hub_core.py +198 -88
  24. lamindb_setup/core/_hub_crud.py +15 -11
  25. lamindb_setup/core/_hub_utils.py +11 -8
  26. lamindb_setup/core/_settings.py +23 -26
  27. lamindb_setup/core/_settings_instance.py +149 -81
  28. lamindb_setup/core/_settings_load.py +12 -7
  29. lamindb_setup/core/_settings_save.py +11 -8
  30. lamindb_setup/core/_settings_storage.py +83 -42
  31. lamindb_setup/core/_settings_store.py +3 -2
  32. lamindb_setup/core/_settings_user.py +10 -6
  33. lamindb_setup/core/_setup_bionty_sources.py +9 -2
  34. lamindb_setup/core/cloud_sqlite_locker.py +13 -10
  35. lamindb_setup/core/django.py +3 -1
  36. lamindb_setup/core/exceptions.py +4 -2
  37. lamindb_setup/core/hashing.py +15 -5
  38. lamindb_setup/core/types.py +5 -2
  39. lamindb_setup/core/upath.py +181 -87
  40. {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.0.dist-info}/METADATA +6 -4
  41. lamindb_setup-0.71.0.dist-info/RECORD +43 -0
  42. lamindb_setup-0.70.0.dist-info/RECORD +0 -43
  43. {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.0.dist-info}/LICENSE +0 -0
  44. {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.0.dist-info}/WHEEL +0 -0
lamindb_setup/__init__.py CHANGED
@@ -1,12 +1,10 @@
1
1
  """Setup & configure LaminDB.
2
2
 
3
- Every function in the API has a matching command in the `lamin` CLI.
4
-
5
- Typically, you'll want to use the CLI rather than this API.
3
+ Many functions in this "setup API" have a matching command in the :doc:`docs:cli` CLI.
6
4
 
7
5
  Guide: :doc:`docs:setup`.
8
6
 
9
- Setup:
7
+ Basic operations:
10
8
 
11
9
  .. autosummary::
12
10
  :toctree:
@@ -17,7 +15,7 @@ Setup:
17
15
  close
18
16
  delete
19
17
 
20
- More instance operations:
18
+ Instance operations:
21
19
 
22
20
  .. autosummary::
23
21
  :toctree:
@@ -36,22 +34,22 @@ Modules & settings:
36
34
 
37
35
  """
38
36
 
39
- __version__ = "0.70.0" # denote a release candidate for 0.1.0 with 0.1rc1
37
+ __version__ = "0.71.0" # denote a release candidate for 0.1.0 with 0.1rc1
40
38
 
41
39
  import sys
42
40
  from os import name as _os_name
43
41
 
44
42
  from . import core
45
- from ._close import close # noqa
46
- from ._delete import delete # noqa
47
- from ._init_instance import init # noqa
48
- from ._connect_instance import connect, load # noqa
49
- from ._migrate import migrate
50
- from ._register_instance import register # noqa
51
- from .core._settings import settings # noqa
52
- from ._setup_user import login, logout # noqa
53
- from ._django import django
54
43
  from ._check_setup import _check_instance_setup
44
+ from ._close import close
45
+ from ._connect_instance import connect, load
46
+ from ._delete import delete
47
+ from ._django import django
48
+ from ._init_instance import init
49
+ from ._migrate import migrate
50
+ from ._register_instance import register
51
+ from ._setup_user import login, logout
52
+ from .core._settings import settings
55
53
 
56
54
  dev = core # backward compat
57
55
  _TESTING = False # used in lamindb tests
@@ -70,3 +68,5 @@ if _os_name == "nt":
70
68
  _original_excepthook(args)
71
69
 
72
70
  threading.excepthook = _except_hook
71
+
72
+ settings.__doc__ = """Global :class:`~lamindb.setup.core.SetupSettings`."""
@@ -1,14 +1,18 @@
1
- from lamin_utils import logger
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
2
4
 
3
- from lamindb_setup.core.types import UPathStr
5
+ from lamin_utils import logger
4
6
 
5
- from ._init_instance import register_user_and_storage
7
+ from ._init_instance import register_storage_in_instance
6
8
  from .core._settings import settings
7
- from .core._settings_instance import InstanceSettings
8
- from .core._settings_storage import StorageSettings
9
+ from .core._settings_storage import init_storage
10
+
11
+ if TYPE_CHECKING:
12
+ from lamindb_setup.core.types import UPathStr
9
13
 
10
14
 
11
- def switch_default_storage(root: UPathStr, **fs_kwargs):
15
+ def add_managed_storage(root: UPathStr, **fs_kwargs):
12
16
  """Add a remote default storage location to a local instance.
13
17
 
14
18
  This can be used to selectively share data.
@@ -17,34 +21,19 @@ def switch_default_storage(root: UPathStr, **fs_kwargs):
17
21
  root: `UPathStr` - The new storage root, e.g., an S3 bucket.
18
22
  **fs_kwargs: Additional fsspec arguments for cloud root, e.g., profile.
19
23
 
20
- Example:
21
- >>> ln.setup.set.storage(
22
- >>> "s3://some-bucket",
23
- >>> profile="some_profile", # fsspec arg
24
- >>> cache_regions=True # fsspec arg for s3
25
- >>> )
26
-
27
24
  """
28
25
  if settings.instance.dialect == "sqlite":
29
- logger.error("can't set storage for sqlite instances.")
30
- return "set-storage-failed"
31
- ssettings = StorageSettings(root=root)
32
- new_isettings = InstanceSettings(
33
- owner=settings.instance.owner,
34
- name=settings.instance.name,
35
- storage=ssettings,
36
- db=settings.instance.db,
37
- schema=settings.instance._schema_str,
38
- id=settings.instance.id,
26
+ raise ValueError(
27
+ "Can't add additional managed storage locations for sqlite instances."
28
+ )
29
+ if not settings.instance.is_on_hub:
30
+ raise ValueError(
31
+ "Can't add additional managed storage locations for instances that aren't managed through the hub."
32
+ )
33
+ ssettings = init_storage(
34
+ root=root, instance_id=settings.instance._id, register_hub=True
39
35
  )
40
-
41
- new_isettings._persist() # this also updates the settings object
42
- register_user_and_storage(new_isettings, settings.user)
43
- # we are not doing this for now because of difficulties to define the right RLS policy # noqa
44
- # https://laminlabs.slack.com/archives/C04FPE8V01W/p1687948324601929?thread_ts=1687531921.394119&cid=C04FPE8V01W
45
- # if settings.instance.is_remote:
46
- # init_storage_hub(
47
- # root, account_handle=settings.instance.owner # type: ignore
48
- # )
49
-
36
+ settings.instance._storage = ssettings
37
+ settings.instance._persist() # this also updates the settings object
38
+ register_storage_in_instance(ssettings)
50
39
  settings.storage._set_fs_kwargs(**fs_kwargs)
lamindb_setup/_cache.py CHANGED
@@ -1,9 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  import shutil
4
+
2
5
  from lamin_utils import logger
3
6
 
4
7
 
5
8
  def clear_cache_dir():
6
- from lamindb_setup import settings, close
9
+ from lamindb_setup import close, settings
7
10
 
8
11
  if settings.instance._is_cloud_sqlite:
9
12
  logger.warning(
lamindb_setup/_check.py CHANGED
@@ -1,3 +1,6 @@
1
+ from __future__ import annotations
2
+
3
+
1
4
  def check():
2
5
  from django.core.management import call_command
3
6
 
@@ -1,12 +1,18 @@
1
- from lamin_utils import logger
2
- from typing import Optional
1
+ from __future__ import annotations
2
+
3
3
  import os
4
+ from typing import TYPE_CHECKING, Optional
5
+
6
+ from lamin_utils import logger
7
+
4
8
  from ._silence_loggers import silence_loggers
9
+ from .core import django
10
+ from .core._settings import settings
5
11
  from .core._settings_store import current_instance_settings_file
6
12
  from .core.exceptions import DefaultMessageException
7
- from .core._settings import settings
8
- from .core._settings_instance import InstanceSettings
9
- from .core import django
13
+
14
+ if TYPE_CHECKING:
15
+ from .core._settings_instance import InstanceSettings
10
16
 
11
17
 
12
18
  class InstanceNotSetupError(DefaultMessageException):
@@ -19,10 +25,10 @@ If you used the CLI to set up lamindb in a notebook, restart the Python session.
19
25
  """
20
26
 
21
27
 
22
- CURRENT_ISETTINGS: Optional[InstanceSettings] = None
28
+ CURRENT_ISETTINGS: InstanceSettings | None = None
23
29
 
24
30
 
25
- def _get_current_instance_settings() -> Optional[InstanceSettings]:
31
+ def _get_current_instance_settings() -> InstanceSettings | None:
26
32
  global CURRENT_ISETTINGS
27
33
 
28
34
  if CURRENT_ISETTINGS is not None:
lamindb_setup/_close.py CHANGED
@@ -1,3 +1,5 @@
1
+ from __future__ import annotations
2
+
1
3
  from lamin_utils import logger
2
4
 
3
5
  from .core._settings import settings
@@ -1,26 +1,32 @@
1
- from pathlib import Path
2
- from typing import Optional, Union, Dict, Tuple
3
- from uuid import UUID
1
+ from __future__ import annotations
2
+
4
3
  import os
4
+ from typing import TYPE_CHECKING
5
+ from uuid import UUID
6
+
5
7
  from lamin_utils import logger
6
- from lamindb_setup.core.types import UPathStr
7
- from lamindb_setup.core._hub_utils import (
8
+
9
+ from ._check_setup import _check_instance_setup
10
+ from ._close import close as close_instance
11
+ from ._init_instance import MESSAGE_NO_MULTIPLE_INSTANCE, load_from_isettings
12
+ from ._migrate import check_whether_migrations_in_sync
13
+ from ._silence_loggers import silence_loggers
14
+ from .core._hub_core import connect_instance as load_instance_from_hub
15
+ from .core._hub_utils import (
8
16
  LaminDsn,
9
17
  LaminDsnModel,
10
18
  )
11
- from ._close import close as close_instance
12
- from ._init_instance import load_from_isettings
13
- from .core._settings import InstanceSettings, settings
14
- from ._silence_loggers import silence_loggers
19
+ from .core._settings import settings
20
+ from .core._settings_instance import InstanceSettings
15
21
  from .core._settings_load import load_instance_settings
16
22
  from .core._settings_storage import StorageSettings
17
23
  from .core._settings_store import instance_settings_file
18
24
  from .core.cloud_sqlite_locker import unlock_cloud_sqlite_upon_exception
19
- from ._init_instance import MESSAGE_NO_MULTIPLE_INSTANCE
20
- from ._check_setup import _check_instance_setup
21
- from .core._hub_core import connect_instance as load_instance_from_hub
22
- from ._migrate import check_whether_migrations_in_sync
23
25
 
26
+ if TYPE_CHECKING:
27
+ from pathlib import Path
28
+
29
+ from .core.types import UPathStr
24
30
 
25
31
  # this is for testing purposes only
26
32
  # set to True only to test failed load
@@ -48,8 +54,8 @@ def check_db_dsn_equal_up_to_credentials(db_dsn_hub, db_dsn_local):
48
54
 
49
55
 
50
56
  def update_db_using_local(
51
- hub_instance_result: Dict[str, str], settings_file: Path, db: Optional[str] = None
52
- ) -> Optional[str]:
57
+ hub_instance_result: dict[str, str], settings_file: Path, db: str | None = None
58
+ ) -> str | None:
53
59
  db_updated = None
54
60
  # check if postgres
55
61
  if hub_instance_result["db_scheme"] == "postgresql":
@@ -98,11 +104,11 @@ def update_db_using_local(
98
104
  def connect(
99
105
  slug: str,
100
106
  *,
101
- db: Optional[str] = None,
102
- storage: Optional[UPathStr] = None,
107
+ db: str | None = None,
108
+ storage: UPathStr | None = None,
103
109
  _raise_not_reachable_error: bool = True,
104
110
  _test: bool = False,
105
- ) -> Optional[Union[str, Tuple]]:
111
+ ) -> str | tuple | None:
106
112
  """Connect to instance.
107
113
 
108
114
  Args:
@@ -133,7 +139,7 @@ def connect(
133
139
  if settings_file.exists():
134
140
  isettings = load_instance_settings(settings_file)
135
141
  # mimic instance_result from hub
136
- instance_result = {"id": isettings.id.hex}
142
+ instance_result = {"id": isettings._id.hex}
137
143
  # skip hub request for a purely local instance
138
144
  make_hub_request = isettings.is_remote
139
145
 
@@ -161,7 +167,8 @@ def connect(
161
167
  db=db_updated,
162
168
  schema=instance_result["schema_str"],
163
169
  git_repo=instance_result["git_repo"],
164
- local_storage=instance_result["storage_mode"] == "hybrid",
170
+ keep_artifacts_local=bool(instance_result["keep_artifacts_local"]),
171
+ is_on_hub=True,
165
172
  )
166
173
  check_whether_migrations_in_sync(instance_result["lamindb_version"])
167
174
  else:
@@ -173,12 +180,12 @@ def connect(
173
180
  if isettings.is_remote:
174
181
  if _raise_not_reachable_error:
175
182
  raise InstanceNotFoundError(message)
176
- return "instance-not-reachable"
183
+ return "instance-not-found"
177
184
 
178
185
  else:
179
186
  if _raise_not_reachable_error:
180
187
  raise InstanceNotFoundError(message)
181
- return "instance-not-reachable"
188
+ return "instance-not-found"
182
189
 
183
190
  if storage is not None:
184
191
  update_isettings_with_storage(isettings, storage)
@@ -203,7 +210,7 @@ def connect(
203
210
  raise SystemExit(msg)
204
211
  else:
205
212
  logger.warning(
206
- f"instance exists with id {isettings.id.hex}, but database is not"
213
+ f"instance exists with id {isettings._id.hex}, but database is not"
207
214
  " loadable: re-initializing"
208
215
  )
209
216
  return "instance-corrupted-or-deleted", instance_result
@@ -224,9 +231,9 @@ def connect(
224
231
  def load(
225
232
  slug: str,
226
233
  *,
227
- db: Optional[str] = None,
228
- storage: Optional[UPathStr] = None,
229
- ) -> Optional[Union[str, Tuple]]:
234
+ db: str | None = None,
235
+ storage: UPathStr | None = None,
236
+ ) -> str | tuple | None:
230
237
  """Connect to instance and set ``auto-connect`` to true.
231
238
 
232
239
  This is exactly the same as ``ln.connect()`` except for that
lamindb_setup/_delete.py CHANGED
@@ -1,17 +1,24 @@
1
+ from __future__ import annotations
2
+
1
3
  import shutil
2
- from pathlib import Path
3
- from lamin_utils import logger
4
+ from typing import TYPE_CHECKING, Optional
4
5
  from uuid import UUID
5
- from typing import Optional
6
- from .core._settings_instance import InstanceSettings
7
- from .core._settings_storage import StorageSettings
6
+
7
+ from lamin_utils import logger
8
+
9
+ from ._connect_instance import INSTANCE_NOT_FOUND_MESSAGE
10
+ from .core._hub_core import connect_instance as load_instance_from_hub
11
+ from .core._hub_core import delete_instance as delete_instance_on_hub
12
+ from .core._hub_core import get_storage_records_for_instance
8
13
  from .core._settings import settings
14
+ from .core._settings_instance import InstanceSettings
9
15
  from .core._settings_load import load_instance_settings
16
+ from .core._settings_storage import StorageSettings
10
17
  from .core._settings_store import instance_settings_file
11
- from .core.upath import check_storage_is_empty, hosted_buckets
12
- from .core._hub_core import delete_instance as delete_instance_on_hub
13
- from .core._hub_core import connect_instance as load_instance_from_hub
14
- from ._connect_instance import INSTANCE_NOT_FOUND_MESSAGE
18
+ from .core.upath import HOSTED_BUCKETS, check_storage_is_empty
19
+
20
+ if TYPE_CHECKING:
21
+ from pathlib import Path
15
22
 
16
23
 
17
24
  def delete_cache(cache_dir: Path):
@@ -20,7 +27,7 @@ def delete_cache(cache_dir: Path):
20
27
 
21
28
 
22
29
  def delete_exclusion_dir(isettings: InstanceSettings) -> None:
23
- exclusion_dir = isettings.storage.root / f".lamindb/_exclusion/{isettings.id.hex}"
30
+ exclusion_dir = isettings.storage.root / f".lamindb/_exclusion/{isettings._id.hex}"
24
31
  if exclusion_dir.exists():
25
32
  exclusion_dir.rmdir()
26
33
 
@@ -45,13 +52,11 @@ def delete_by_isettings(isettings: InstanceSettings) -> None:
45
52
  if settings._instance_settings_path.exists():
46
53
  settings._instance_settings_path.unlink()
47
54
  settings._instance_settings = None
48
- if isettings.storage._mark_storage_root.exists():
49
- isettings.storage._mark_storage_root.unlink()
50
55
 
51
56
 
52
57
  def delete(
53
58
  instance_name: str, force: bool = False, require_empty: bool = True
54
- ) -> Optional[int]:
59
+ ) -> int | None:
55
60
  """Delete a LaminDB instance.
56
61
 
57
62
  Args:
@@ -94,7 +99,7 @@ def delete(
94
99
  owner=settings.user.handle,
95
100
  name=instance_name,
96
101
  storage=ssettings,
97
- local_storage=instance_result["storage_mode"] == "hybrid",
102
+ keep_artifacts_local=bool(instance_result["keep_artifacts_local"]),
98
103
  db=instance_result["db"] if "db" in instance_result else None,
99
104
  schema=instance_result["schema_str"],
100
105
  git_repo=instance_result["git_repo"],
@@ -105,6 +110,10 @@ def delete(
105
110
  logger.warning(
106
111
  f"delete() does not yet affect your Postgres database at {isettings.db}"
107
112
  )
113
+ if isettings.is_on_hub and settings.user.handle == "anonymous":
114
+ logger.warning(
115
+ "won't delete the hub component of this instance because you're not logged in"
116
+ )
108
117
  if not force:
109
118
  valid_responses = ["y", "yes"]
110
119
  user_input = (
@@ -120,25 +129,49 @@ def delete(
120
129
  # delete the exlusion dir first because it's hard to count its objects
121
130
  delete_exclusion_dir(isettings)
122
131
  if isettings.storage.type_is_cloud and isettings.storage.root_as_str.startswith(
123
- hosted_buckets
132
+ HOSTED_BUCKETS
124
133
  ):
125
134
  if not require_empty:
126
135
  logger.warning(
127
136
  "hosted storage always has to be empty, ignoring `require_empty`"
128
137
  )
129
138
  require_empty = True
139
+ # first the default storage
130
140
  n_objects = check_storage_is_empty(
131
141
  isettings.storage.root,
132
142
  raise_error=require_empty,
133
143
  account_for_sqlite_file=isettings.dialect == "sqlite",
134
144
  )
145
+ if isettings.storage._mark_storage_root.exists():
146
+ isettings.storage._mark_storage_root.unlink(
147
+ missing_ok=True # this is totally weird, but needed on Py3.11
148
+ )
149
+ # now everything that's on the hub
150
+ if settings.user.handle != "anonymous":
151
+ storage_records = get_storage_records_for_instance(isettings._id)
152
+ for storage_record in storage_records:
153
+ if storage_record["root"] == isettings.storage.root_as_str:
154
+ continue
155
+ check_storage_is_empty(
156
+ storage_record["root"], # type: ignore
157
+ raise_error=require_empty,
158
+ )
159
+ ssettings = StorageSettings(storage_record["root"]) # type: ignore
160
+ if ssettings._mark_storage_root.exists():
161
+ ssettings._mark_storage_root.unlink(
162
+ missing_ok=True # this is totally weird, but needed on Py3.11
163
+ )
135
164
  logger.info(f"deleting instance {instance_slug}")
136
165
  # below we can skip check_storage_is_empty() because we already called
137
166
  # it above
138
- delete_instance_on_hub(isettings.id, require_empty=False)
167
+ if settings.user.handle != "anonymous":
168
+ delete_instance_on_hub(isettings._id, require_empty=False)
139
169
  delete_by_isettings(isettings)
140
- if n_objects == 0 and isettings.storage.type == "local":
170
+ # if .lndb file was delete, then we might count -1
171
+ if n_objects <= 0 and isettings.storage.type == "local":
141
172
  # dir is only empty after sqlite file was delete via delete_by_isettings
142
- (isettings.storage.root / ".lamindb").rmdir()
143
- isettings.storage.root.rmdir()
173
+ if (isettings.storage.root / ".lamindb").exists():
174
+ (isettings.storage.root / ".lamindb").rmdir()
175
+ if isettings.storage.root.exists():
176
+ isettings.storage.root.rmdir()
144
177
  return None
lamindb_setup/_django.py CHANGED
@@ -1,9 +1,12 @@
1
+ from __future__ import annotations
2
+
1
3
  from typing import Optional
4
+
2
5
  from .core._settings import settings
3
6
  from .core.django import setup_django
4
7
 
5
8
 
6
- def django(command: str, package_name: Optional[str] = None, **kwargs):
9
+ def django(command: str, package_name: str | None = None, **kwargs):
7
10
  r"""Manage migrations.
8
11
 
9
12
  Examples:
@@ -1,6 +1,7 @@
1
- from pathlib import Path
2
- from importlib import import_module
1
+ from __future__ import annotations
3
2
 
3
+ from importlib import import_module
4
+ from pathlib import Path
4
5
 
5
6
  MODELS = {
6
7
  "core": {
@@ -46,6 +47,7 @@ def exportdb() -> None:
46
47
  directory = Path("./lamindb_export/")
47
48
  directory.mkdir(parents=True, exist_ok=True)
48
49
  import pandas as pd
50
+
49
51
  import lamindb_setup as ln_setup
50
52
 
51
53
  def export_registry(registry, directory):
@@ -1,6 +1,9 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib import import_module
1
4
  from pathlib import Path
5
+
2
6
  from ._exportdb import MODELS
3
- from importlib import import_module
4
7
 
5
8
 
6
9
  def import_registry(registry, directory, connection):
@@ -26,6 +29,7 @@ def importdb() -> None:
26
29
  if response != "y":
27
30
  return None
28
31
  from sqlalchemy import create_engine, text
32
+
29
33
  import lamindb_setup as ln_setup
30
34
 
31
35
  engine = create_engine(ln_setup.settings.instance.db, echo=False)