lamindb_setup 0.69.5__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 +50 -34
  8. lamindb_setup/_delete.py +121 -22
  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 +58 -46
  13. lamindb_setup/_migrate.py +20 -14
  14. lamindb_setup/_register_instance.py +10 -3
  15. lamindb_setup/_schema.py +6 -3
  16. lamindb_setup/_setup_user.py +8 -8
  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 +206 -85
  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 +164 -42
  28. lamindb_setup/core/_settings_load.py +13 -8
  29. lamindb_setup/core/_settings_save.py +11 -8
  30. lamindb_setup/core/_settings_storage.py +104 -95
  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 +5 -0
  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 +202 -92
  40. {lamindb_setup-0.69.5.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.69.5.dist-info/RECORD +0 -43
  43. {lamindb_setup-0.69.5.dist-info → lamindb_setup-0.71.0.dist-info}/LICENSE +0 -0
  44. {lamindb_setup-0.69.5.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.69.5" # 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,32 +1,49 @@
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
27
33
  _TEST_FAILED_LOAD = False
28
34
 
29
35
 
36
+ INSTANCE_NOT_FOUND_MESSAGE = (
37
+ "'{owner}/{name}' not found:"
38
+ " '{hub_result}'\nCheck your permissions:"
39
+ " https://lamin.ai/{owner}/{name}"
40
+ )
41
+
42
+
43
+ class InstanceNotFoundError(SystemExit):
44
+ pass
45
+
46
+
30
47
  def check_db_dsn_equal_up_to_credentials(db_dsn_hub, db_dsn_local):
31
48
  return (
32
49
  db_dsn_hub.scheme == db_dsn_local.scheme
@@ -37,8 +54,8 @@ def check_db_dsn_equal_up_to_credentials(db_dsn_hub, db_dsn_local):
37
54
 
38
55
 
39
56
  def update_db_using_local(
40
- hub_instance_result: Dict[str, str], settings_file: Path, db: Optional[str] = None
41
- ) -> Optional[str]:
57
+ hub_instance_result: dict[str, str], settings_file: Path, db: str | None = None
58
+ ) -> str | None:
42
59
  db_updated = None
43
60
  # check if postgres
44
61
  if hub_instance_result["db_scheme"] == "postgresql":
@@ -87,11 +104,11 @@ def update_db_using_local(
87
104
  def connect(
88
105
  slug: str,
89
106
  *,
90
- db: Optional[str] = None,
91
- storage: Optional[UPathStr] = None,
107
+ db: str | None = None,
108
+ storage: UPathStr | None = None,
92
109
  _raise_not_reachable_error: bool = True,
93
110
  _test: bool = False,
94
- ) -> Optional[Union[str, Tuple]]:
111
+ ) -> str | tuple | None:
95
112
  """Connect to instance.
96
113
 
97
114
  Args:
@@ -121,6 +138,8 @@ def connect(
121
138
  make_hub_request = True
122
139
  if settings_file.exists():
123
140
  isettings = load_instance_settings(settings_file)
141
+ # mimic instance_result from hub
142
+ instance_result = {"id": isettings._id.hex}
124
143
  # skip hub request for a purely local instance
125
144
  make_hub_request = isettings.is_remote
126
145
 
@@ -139,7 +158,6 @@ def connect(
139
158
  root=storage_result["root"],
140
159
  region=storage_result["region"],
141
160
  uid=storage_result["lnid"],
142
- is_hybrid=instance_result["storage_mode"] == "hybrid",
143
161
  )
144
162
  isettings = InstanceSettings(
145
163
  id=UUID(instance_result["id"]),
@@ -149,27 +167,25 @@ def connect(
149
167
  db=db_updated,
150
168
  schema=instance_result["schema_str"],
151
169
  git_repo=instance_result["git_repo"],
170
+ keep_artifacts_local=bool(instance_result["keep_artifacts_local"]),
171
+ is_on_hub=True,
152
172
  )
153
173
  check_whether_migrations_in_sync(instance_result["lamindb_version"])
154
174
  else:
155
- error_message = (
156
- f"'{owner}/{name}' not loadable:"
157
- f" '{hub_result}'\nCheck your permissions:"
158
- f" https://lamin.ai/{owner}/{name}?tab=collaborators"
175
+ message = INSTANCE_NOT_FOUND_MESSAGE.format(
176
+ owner=owner, name=name, hub_result=hub_result
159
177
  )
160
178
  if settings_file.exists():
161
179
  isettings = load_instance_settings(settings_file)
162
180
  if isettings.is_remote:
163
181
  if _raise_not_reachable_error:
164
- raise SystemExit(error_message)
165
- return "instance-not-reachable"
182
+ raise InstanceNotFoundError(message)
183
+ return "instance-not-found"
166
184
 
167
185
  else:
168
186
  if _raise_not_reachable_error:
169
- raise SystemExit(error_message)
170
- return "instance-not-reachable"
171
- # mimic instance_result from hub
172
- instance_result = {"id": isettings.id.hex}
187
+ raise InstanceNotFoundError(message)
188
+ return "instance-not-found"
173
189
 
174
190
  if storage is not None:
175
191
  update_isettings_with_storage(isettings, storage)
@@ -194,7 +210,7 @@ def connect(
194
210
  raise SystemExit(msg)
195
211
  else:
196
212
  logger.warning(
197
- 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"
198
214
  " loadable: re-initializing"
199
215
  )
200
216
  return "instance-corrupted-or-deleted", instance_result
@@ -215,9 +231,9 @@ def connect(
215
231
  def load(
216
232
  slug: str,
217
233
  *,
218
- db: Optional[str] = None,
219
- storage: Optional[UPathStr] = None,
220
- ) -> Optional[Union[str, Tuple]]:
234
+ db: str | None = None,
235
+ storage: UPathStr | None = None,
236
+ ) -> str | tuple | None:
221
237
  """Connect to instance and set ``auto-connect`` to true.
222
238
 
223
239
  This is exactly the same as ``ln.connect()`` except for that
@@ -255,7 +271,7 @@ def update_isettings_with_storage(
255
271
  isettings: InstanceSettings, storage: UPathStr
256
272
  ) -> None:
257
273
  ssettings = StorageSettings(storage)
258
- if ssettings.is_cloud:
274
+ if ssettings.type_is_cloud:
259
275
  try: # triggering ssettings.id makes a lookup in the storage table
260
276
  logger.success(f"loaded storage: {ssettings.id} / {ssettings.root_as_str}")
261
277
  except RuntimeError as e:
lamindb_setup/_delete.py CHANGED
@@ -1,11 +1,24 @@
1
+ from __future__ import annotations
2
+
1
3
  import shutil
2
- from pathlib import Path
4
+ from typing import TYPE_CHECKING, Optional
5
+ from uuid import UUID
6
+
3
7
  from lamin_utils import logger
4
- from typing import Optional
5
- from .core._settings_instance import InstanceSettings
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
6
13
  from .core._settings import settings
14
+ from .core._settings_instance import InstanceSettings
7
15
  from .core._settings_load import load_instance_settings
16
+ from .core._settings_storage import StorageSettings
8
17
  from .core._settings_store import instance_settings_file
18
+ from .core.upath import HOSTED_BUCKETS, check_storage_is_empty
19
+
20
+ if TYPE_CHECKING:
21
+ from pathlib import Path
9
22
 
10
23
 
11
24
  def delete_cache(cache_dir: Path):
@@ -13,6 +26,12 @@ def delete_cache(cache_dir: Path):
13
26
  shutil.rmtree(cache_dir)
14
27
 
15
28
 
29
+ def delete_exclusion_dir(isettings: InstanceSettings) -> None:
30
+ exclusion_dir = isettings.storage.root / f".lamindb/_exclusion/{isettings._id.hex}"
31
+ if exclusion_dir.exists():
32
+ exclusion_dir.rmdir()
33
+
34
+
16
35
  def delete_by_isettings(isettings: InstanceSettings) -> None:
17
36
  settings_file = isettings._get_settings_file()
18
37
  if settings_file.exists():
@@ -22,20 +41,12 @@ def delete_by_isettings(isettings: InstanceSettings) -> None:
22
41
  try:
23
42
  if isettings._sqlite_file.exists():
24
43
  isettings._sqlite_file.unlink()
25
- exclusion_dir = (
26
- isettings.storage.root / f".lamindb/_exclusion/{isettings.id.hex}"
27
- )
28
- if exclusion_dir.exists():
29
- exclusion_dir.rmdir()
30
44
  except PermissionError:
31
45
  logger.warning(
32
46
  "Did not have permission to delete SQLite file:"
33
47
  f" {isettings._sqlite_file}"
34
48
  )
35
49
  pass
36
- if isettings.is_remote:
37
- logger.warning("manually delete your remote instance on lamin.ai")
38
- logger.warning(f"manually delete your stored data: {isettings.storage.root}")
39
50
  # unset the global instance settings
40
51
  if settings._instance_exists and isettings.slug == settings.instance.slug:
41
52
  if settings._instance_settings_path.exists():
@@ -43,8 +54,16 @@ def delete_by_isettings(isettings: InstanceSettings) -> None:
43
54
  settings._instance_settings = None
44
55
 
45
56
 
46
- def delete(instance_name: str, force: bool = False) -> Optional[int]:
47
- """Delete a LaminDB instance."""
57
+ def delete(
58
+ instance_name: str, force: bool = False, require_empty: bool = True
59
+ ) -> int | None:
60
+ """Delete a LaminDB instance.
61
+
62
+ Args:
63
+ instance_name (str): The name of the instance to delete.
64
+ force (bool): Whether to skip the confirmation prompt.
65
+ require_empty (bool): Whether to check if the instance is empty before deleting.
66
+ """
48
67
  if "/" in instance_name:
49
68
  logger.warning(
50
69
  "Deleting the instance of another user is currently not supported with the"
@@ -53,6 +72,48 @@ def delete(instance_name: str, force: bool = False) -> Optional[int]:
53
72
  )
54
73
  raise ValueError("Invalid instance name: '/' delimiter not allowed.")
55
74
  instance_slug = f"{settings.user.handle}/{instance_name}"
75
+ if settings._instance_exists and settings.instance.name == instance_name:
76
+ isettings = settings.instance
77
+ else:
78
+ settings_file = instance_settings_file(instance_name, settings.user.handle)
79
+ if not settings_file.exists():
80
+ hub_result = load_instance_from_hub(
81
+ owner=settings.user.handle, name=instance_name
82
+ )
83
+ if isinstance(hub_result, str):
84
+ message = INSTANCE_NOT_FOUND_MESSAGE.format(
85
+ owner=settings.user.handle,
86
+ name=instance_name,
87
+ hub_result=hub_result,
88
+ )
89
+ logger.warning(message)
90
+ return None
91
+ instance_result, storage_result = hub_result
92
+ ssettings = StorageSettings(
93
+ root=storage_result["root"],
94
+ region=storage_result["region"],
95
+ uid=storage_result["lnid"],
96
+ )
97
+ isettings = InstanceSettings(
98
+ id=UUID(instance_result["id"]),
99
+ owner=settings.user.handle,
100
+ name=instance_name,
101
+ storage=ssettings,
102
+ keep_artifacts_local=bool(instance_result["keep_artifacts_local"]),
103
+ db=instance_result["db"] if "db" in instance_result else None,
104
+ schema=instance_result["schema_str"],
105
+ git_repo=instance_result["git_repo"],
106
+ )
107
+ else:
108
+ isettings = load_instance_settings(settings_file)
109
+ if isettings.dialect != "sqlite":
110
+ logger.warning(
111
+ f"delete() does not yet affect your Postgres database at {isettings.db}"
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
+ )
56
117
  if not force:
57
118
  valid_responses = ["y", "yes"]
58
119
  user_input = (
@@ -62,17 +123,55 @@ def delete(instance_name: str, force: bool = False) -> Optional[int]:
62
123
  )
63
124
  if user_input not in valid_responses:
64
125
  return -1
65
- if settings._instance_exists and settings.instance.name == instance_name:
66
- isettings = settings.instance
67
- else:
68
- settings_file = instance_settings_file(instance_name, settings.user.handle)
69
- if not settings_file.exists():
126
+
127
+ # the actual deletion process begins here
128
+ if isettings.dialect == "sqlite" and isettings.is_remote:
129
+ # delete the exlusion dir first because it's hard to count its objects
130
+ delete_exclusion_dir(isettings)
131
+ if isettings.storage.type_is_cloud and isettings.storage.root_as_str.startswith(
132
+ HOSTED_BUCKETS
133
+ ):
134
+ if not require_empty:
70
135
  logger.warning(
71
- "could not delete as instance settings do not exist locally. did you"
72
- " provide a wrong instance name? could you try loading it?"
136
+ "hosted storage always has to be empty, ignoring `require_empty`"
137
+ )
138
+ require_empty = True
139
+ # first the default storage
140
+ n_objects = check_storage_is_empty(
141
+ isettings.storage.root,
142
+ raise_error=require_empty,
143
+ account_for_sqlite_file=isettings.dialect == "sqlite",
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,
73
158
  )
74
- return None
75
- isettings = load_instance_settings(settings_file)
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
+ )
76
164
  logger.info(f"deleting instance {instance_slug}")
165
+ # below we can skip check_storage_is_empty() because we already called
166
+ # it above
167
+ if settings.user.handle != "anonymous":
168
+ delete_instance_on_hub(isettings._id, require_empty=False)
77
169
  delete_by_isettings(isettings)
170
+ # if .lndb file was delete, then we might count -1
171
+ if n_objects <= 0 and isettings.storage.type == "local":
172
+ # dir is only empty after sqlite file was delete via delete_by_isettings
173
+ if (isettings.storage.root / ".lamindb").exists():
174
+ (isettings.storage.root / ".lamindb").rmdir()
175
+ if isettings.storage.root.exists():
176
+ isettings.storage.root.rmdir()
78
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)