lamindb_setup 1.19.0__py3-none-any.whl → 1.19.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. lamindb_setup/__init__.py +1 -1
  2. lamindb_setup/_cache.py +87 -87
  3. lamindb_setup/_check.py +7 -7
  4. lamindb_setup/_check_setup.py +131 -131
  5. lamindb_setup/_connect_instance.py +443 -441
  6. lamindb_setup/_delete.py +155 -155
  7. lamindb_setup/_disconnect.py +38 -38
  8. lamindb_setup/_django.py +39 -39
  9. lamindb_setup/_entry_points.py +19 -19
  10. lamindb_setup/_init_instance.py +423 -423
  11. lamindb_setup/_migrate.py +331 -331
  12. lamindb_setup/_register_instance.py +32 -32
  13. lamindb_setup/_schema.py +27 -27
  14. lamindb_setup/_schema_metadata.py +451 -451
  15. lamindb_setup/_set_managed_storage.py +81 -81
  16. lamindb_setup/_setup_user.py +198 -198
  17. lamindb_setup/_silence_loggers.py +46 -46
  18. lamindb_setup/core/__init__.py +25 -34
  19. lamindb_setup/core/_aws_options.py +276 -276
  20. lamindb_setup/core/_aws_storage.py +57 -57
  21. lamindb_setup/core/_clone.py +50 -50
  22. lamindb_setup/core/_deprecated.py +62 -62
  23. lamindb_setup/core/_docs.py +14 -14
  24. lamindb_setup/core/_hub_client.py +288 -288
  25. lamindb_setup/core/_hub_crud.py +247 -247
  26. lamindb_setup/core/_hub_utils.py +100 -100
  27. lamindb_setup/core/_private_django_api.py +80 -80
  28. lamindb_setup/core/_settings.py +440 -434
  29. lamindb_setup/core/_settings_instance.py +22 -1
  30. lamindb_setup/core/_settings_load.py +162 -162
  31. lamindb_setup/core/_settings_save.py +108 -108
  32. lamindb_setup/core/_settings_storage.py +433 -433
  33. lamindb_setup/core/_settings_store.py +162 -162
  34. lamindb_setup/core/_settings_user.py +55 -55
  35. lamindb_setup/core/_setup_bionty_sources.py +44 -44
  36. lamindb_setup/core/cloud_sqlite_locker.py +240 -240
  37. lamindb_setup/core/django.py +414 -413
  38. lamindb_setup/core/exceptions.py +1 -1
  39. lamindb_setup/core/hashing.py +134 -134
  40. lamindb_setup/core/types.py +1 -1
  41. lamindb_setup/core/upath.py +1031 -1028
  42. lamindb_setup/errors.py +72 -72
  43. lamindb_setup/io.py +423 -423
  44. lamindb_setup/types.py +17 -17
  45. {lamindb_setup-1.19.0.dist-info → lamindb_setup-1.19.1.dist-info}/METADATA +3 -2
  46. lamindb_setup-1.19.1.dist-info/RECORD +51 -0
  47. {lamindb_setup-1.19.0.dist-info → lamindb_setup-1.19.1.dist-info}/WHEEL +1 -1
  48. {lamindb_setup-1.19.0.dist-info → lamindb_setup-1.19.1.dist-info/licenses}/LICENSE +201 -201
  49. lamindb_setup-1.19.0.dist-info/RECORD +0 -51
lamindb_setup/_delete.py CHANGED
@@ -1,155 +1,155 @@
1
- from __future__ import annotations
2
-
3
- import shutil
4
- from typing import TYPE_CHECKING
5
- from uuid import UUID
6
-
7
- from lamin_utils import logger
8
-
9
- from ._connect_instance import _connect_instance, get_owner_name_from_identifier
10
- from .core._aws_options import HOSTED_BUCKETS
11
- from .core._settings import settings
12
- from .core._settings_load import load_instance_settings
13
- from .core._settings_storage import StorageSettings
14
- from .core.upath import LocalPathClasses, check_storage_is_empty
15
-
16
- if TYPE_CHECKING:
17
- from pathlib import Path
18
-
19
- from .core._settings_instance import InstanceSettings
20
-
21
-
22
- def delete_cache(isettings: InstanceSettings):
23
- if isettings.storage is None:
24
- return
25
- # avoid init of root
26
- root = isettings.storage._root_init
27
- if not isinstance(root, LocalPathClasses):
28
- cache_dir = settings.cache_dir / root.path
29
- if cache_dir.exists():
30
- shutil.rmtree(cache_dir)
31
-
32
-
33
- def delete_exclusion_dir(isettings: InstanceSettings) -> None:
34
- exclusion_dir = isettings.storage.root / f".lamindb/_exclusion/{isettings._id.hex}"
35
- if exclusion_dir.exists():
36
- exclusion_dir.rmdir()
37
-
38
-
39
- def delete_by_isettings(isettings: InstanceSettings) -> None:
40
- assert isettings.slug != "none/none"
41
-
42
- settings_file = isettings._get_settings_file()
43
- if settings_file.exists():
44
- settings_file.unlink()
45
- delete_cache(isettings)
46
- if isettings.dialect == "sqlite" and isettings.storage is not None:
47
- try:
48
- if isettings._sqlite_file.exists():
49
- isettings._sqlite_file.unlink()
50
- except PermissionError:
51
- logger.warning(
52
- "Did not have permission to delete SQLite file:"
53
- f" {isettings._sqlite_file}"
54
- )
55
- # unset the global instance settings
56
- isettings_on_disk = load_instance_settings()
57
- if isettings_on_disk.slug == isettings.slug:
58
- settings._instance_settings_path.unlink() # current instance settings file
59
- # settings.instance can differ from instance in current_settings_file()
60
- # due to connect() in the same process
61
- if settings.instance.slug == isettings.slug:
62
- settings._instance_settings = None
63
-
64
-
65
- def delete(slug: str, force: bool = False, require_empty: bool = True) -> int | None:
66
- """Delete a LaminDB instance.
67
-
68
- Args:
69
- slug: The instance slug `account_handle/instance_name` or URL.
70
- If the instance is owned by you, it suffices to pass the instance name.
71
- force: Whether to skip the confirmation prompt.
72
- require_empty: Whether to check if the instance is empty before deleting.
73
-
74
- See Also:
75
- Delete an instance via the CLI, see `here <https://docs.lamin.ai/cli#delete>`__.
76
- """
77
- owner, name = get_owner_name_from_identifier(slug)
78
- isettings = _connect_instance(owner, name, raise_permission_error=False)
79
- if isettings.dialect != "sqlite":
80
- logger.warning(
81
- f"delete() does not yet affect your Postgres database at {isettings.db}"
82
- )
83
- if isettings.is_on_hub and settings.user.handle == "anonymous":
84
- logger.warning(
85
- "won't delete the hub component of this instance because you're not logged in"
86
- )
87
- if not force:
88
- valid_responses = ["y", "yes"]
89
- user_input = (
90
- input(f"Are you sure you want to delete instance {isettings.slug}? (y/n) ")
91
- .strip()
92
- .lower()
93
- )
94
- if user_input not in valid_responses:
95
- return -1
96
-
97
- # the actual deletion process begins here
98
- if isettings.dialect == "sqlite" and isettings.is_remote:
99
- # delete the exlusion dir first because it's hard to count its objects
100
- delete_exclusion_dir(isettings)
101
- if isettings.storage.type_is_cloud and isettings.storage.root_as_str.startswith(
102
- HOSTED_BUCKETS
103
- ):
104
- if not require_empty:
105
- logger.warning(
106
- "hosted storage always has to be empty, ignoring `require_empty`"
107
- )
108
- require_empty = True
109
- # first the default storage
110
- n_files = check_storage_is_empty(
111
- isettings.storage.root,
112
- raise_error=require_empty,
113
- account_for_sqlite_file=isettings.dialect == "sqlite",
114
- )
115
- if isettings.storage._mark_storage_root.exists():
116
- isettings.storage._mark_storage_root.unlink(
117
- missing_ok=True # this is totally weird, but needed on Py3.11
118
- )
119
- # now everything that's on the hub
120
- if settings.user.handle != "anonymous":
121
- # dynamic import to avoid importing hub logic at root
122
- from .core._hub_core import get_storage_records_for_instance
123
-
124
- storage_records = get_storage_records_for_instance(isettings._id)
125
- for storage_record in storage_records:
126
- if storage_record["root"] == isettings.storage.root_as_str:
127
- continue
128
- ssettings = StorageSettings(storage_record["root"]) # type: ignore
129
- check_storage_is_empty(
130
- ssettings.root, # type: ignore
131
- raise_error=require_empty,
132
- )
133
- if ssettings._mark_storage_root.exists():
134
- ssettings._mark_storage_root.unlink(
135
- missing_ok=True # this is totally weird, but needed on Py3.11
136
- )
137
- logger.info(f"deleting instance {isettings.slug}")
138
- # below we can skip check_storage_is_empty() because we already called
139
- # it above
140
- if settings.user.handle != "anonymous" and isettings.is_on_hub:
141
- # start with deleting things on the hub
142
- # this will error if the user doesn't have permission
143
- # dynamic import to avoid importing hub logic at root
144
- from .core._hub_core import delete_instance
145
-
146
- delete_instance(isettings._id, require_empty=False)
147
- delete_by_isettings(isettings)
148
- # if lamin.db file was delete, then we might count -1
149
- if n_files <= 0 and isettings.storage.type == "local":
150
- # dir is only empty after sqlite file was delete via delete_by_isettings
151
- if (isettings.storage.root / ".lamindb").exists():
152
- (isettings.storage.root / ".lamindb").rmdir()
153
- if isettings.storage.root.exists():
154
- isettings.storage.root.rmdir()
155
- return None
1
+ from __future__ import annotations
2
+
3
+ import shutil
4
+ from typing import TYPE_CHECKING
5
+ from uuid import UUID
6
+
7
+ from lamin_utils import logger
8
+
9
+ from ._connect_instance import _connect_instance, get_owner_name_from_identifier
10
+ from .core._aws_options import HOSTED_BUCKETS
11
+ from .core._settings import settings
12
+ from .core._settings_load import load_instance_settings
13
+ from .core._settings_storage import StorageSettings
14
+ from .core.upath import LocalPathClasses, check_storage_is_empty
15
+
16
+ if TYPE_CHECKING:
17
+ from pathlib import Path
18
+
19
+ from .core._settings_instance import InstanceSettings
20
+
21
+
22
+ def delete_cache(isettings: InstanceSettings):
23
+ if isettings.storage is None:
24
+ return
25
+ # avoid init of root
26
+ root = isettings.storage._root_init
27
+ if not isinstance(root, LocalPathClasses):
28
+ cache_dir = settings.cache_dir / root.path
29
+ if cache_dir.exists():
30
+ shutil.rmtree(cache_dir)
31
+
32
+
33
+ def delete_exclusion_dir(isettings: InstanceSettings) -> None:
34
+ exclusion_dir = isettings.storage.root / f".lamindb/_exclusion/{isettings._id.hex}"
35
+ if exclusion_dir.exists():
36
+ exclusion_dir.rmdir()
37
+
38
+
39
+ def delete_by_isettings(isettings: InstanceSettings) -> None:
40
+ assert isettings.slug != "none/none"
41
+
42
+ settings_file = isettings._get_settings_file()
43
+ if settings_file.exists():
44
+ settings_file.unlink()
45
+ delete_cache(isettings)
46
+ if isettings.dialect == "sqlite" and isettings.storage is not None:
47
+ try:
48
+ if isettings._sqlite_file.exists():
49
+ isettings._sqlite_file.unlink()
50
+ except PermissionError:
51
+ logger.warning(
52
+ "Did not have permission to delete SQLite file:"
53
+ f" {isettings._sqlite_file}"
54
+ )
55
+ # unset the global instance settings
56
+ isettings_on_disk = load_instance_settings()
57
+ if isettings_on_disk.slug == isettings.slug:
58
+ settings._instance_settings_path.unlink() # current instance settings file
59
+ # settings.instance can differ from instance in current_settings_file()
60
+ # due to connect() in the same process
61
+ if settings.instance.slug == isettings.slug:
62
+ settings._instance_settings = None
63
+
64
+
65
+ def delete(slug: str, force: bool = False, require_empty: bool = True) -> int | None:
66
+ """Delete a LaminDB instance.
67
+
68
+ Args:
69
+ slug: The instance slug `account_handle/instance_name` or URL.
70
+ If the instance is owned by you, it suffices to pass the instance name.
71
+ force: Whether to skip the confirmation prompt.
72
+ require_empty: Whether to check if the instance is empty before deleting.
73
+
74
+ See Also:
75
+ Delete an instance via the CLI, see `here <https://docs.lamin.ai/cli#delete>`__.
76
+ """
77
+ owner, name = get_owner_name_from_identifier(slug)
78
+ isettings = _connect_instance(owner, name, raise_permission_error=False)
79
+ if isettings.dialect != "sqlite":
80
+ logger.warning(
81
+ f"delete() does not yet affect your Postgres database at {isettings.db}"
82
+ )
83
+ if isettings.is_on_hub and settings.user.handle == "anonymous":
84
+ logger.warning(
85
+ "won't delete the hub component of this instance because you're not logged in"
86
+ )
87
+ if not force:
88
+ valid_responses = ["y", "yes"]
89
+ user_input = (
90
+ input(f"Are you sure you want to delete instance {isettings.slug}? (y/n) ")
91
+ .strip()
92
+ .lower()
93
+ )
94
+ if user_input not in valid_responses:
95
+ return -1
96
+
97
+ # the actual deletion process begins here
98
+ if isettings.dialect == "sqlite" and isettings.is_remote:
99
+ # delete the exlusion dir first because it's hard to count its objects
100
+ delete_exclusion_dir(isettings)
101
+ if isettings.storage.type_is_cloud and isettings.storage.root_as_str.startswith(
102
+ HOSTED_BUCKETS
103
+ ):
104
+ if not require_empty:
105
+ logger.warning(
106
+ "hosted storage always has to be empty, ignoring `require_empty`"
107
+ )
108
+ require_empty = True
109
+ # first the default storage
110
+ n_files = check_storage_is_empty(
111
+ isettings.storage.root,
112
+ raise_error=require_empty,
113
+ account_for_sqlite_file=isettings.dialect == "sqlite",
114
+ )
115
+ if isettings.storage._mark_storage_root.exists():
116
+ isettings.storage._mark_storage_root.unlink(
117
+ missing_ok=True # this is totally weird, but needed on Py3.11
118
+ )
119
+ # now everything that's on the hub
120
+ if settings.user.handle != "anonymous":
121
+ # dynamic import to avoid importing hub logic at root
122
+ from .core._hub_core import get_storage_records_for_instance
123
+
124
+ storage_records = get_storage_records_for_instance(isettings._id)
125
+ for storage_record in storage_records:
126
+ if storage_record["root"] == isettings.storage.root_as_str:
127
+ continue
128
+ ssettings = StorageSettings(storage_record["root"]) # type: ignore
129
+ check_storage_is_empty(
130
+ ssettings.root, # type: ignore
131
+ raise_error=require_empty,
132
+ )
133
+ if ssettings._mark_storage_root.exists():
134
+ ssettings._mark_storage_root.unlink(
135
+ missing_ok=True # this is totally weird, but needed on Py3.11
136
+ )
137
+ logger.info(f"deleting instance {isettings.slug}")
138
+ # below we can skip check_storage_is_empty() because we already called
139
+ # it above
140
+ if settings.user.handle != "anonymous" and isettings.is_on_hub:
141
+ # start with deleting things on the hub
142
+ # this will error if the user doesn't have permission
143
+ # dynamic import to avoid importing hub logic at root
144
+ from .core._hub_core import delete_instance
145
+
146
+ delete_instance(isettings._id, require_empty=False)
147
+ delete_by_isettings(isettings)
148
+ # if lamin.db file was delete, then we might count -1
149
+ if n_files <= 0 and isettings.storage.type == "local":
150
+ # dir is only empty after sqlite file was delete via delete_by_isettings
151
+ if (isettings.storage.root / ".lamindb").exists():
152
+ (isettings.storage.root / ".lamindb").rmdir()
153
+ if isettings.storage.root.exists():
154
+ isettings.storage.root.rmdir()
155
+ return None
@@ -1,38 +1,38 @@
1
- from __future__ import annotations
2
-
3
- from lamin_utils import logger
4
-
5
- from .core._settings import settings
6
- from .core._settings_load import load_instance_settings
7
- from .core.cloud_sqlite_locker import clear_locker
8
-
9
-
10
- def disconnect(mute: bool = False) -> None:
11
- """Clear default instance configuration.
12
-
13
- Returns `None` if succeeds, otherwise an exception is raised.
14
-
15
- See Also:
16
- Clear default instance configuration via the CLI, see `here <https://docs.lamin.ai/cli#disconnect>`__.
17
- """
18
- # settings._instance_exists can be true due to connect even without having a file
19
- if settings._instance_exists:
20
- instance = settings.instance
21
- try:
22
- instance._update_cloud_sqlite_file()
23
- except Exception as e:
24
- if isinstance(e, FileNotFoundError):
25
- logger.warning("did not find local cache file")
26
- elif isinstance(e, PermissionError):
27
- logger.warning("did not upload cache file - not enough permissions")
28
- else:
29
- raise e
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
35
- if not mute:
36
- logger.success(f"disconnected instance: {instance.slug}")
37
- elif not mute:
38
- logger.info("no instance loaded")
1
+ from __future__ import annotations
2
+
3
+ from lamin_utils import logger
4
+
5
+ from .core._settings import settings
6
+ from .core._settings_load import load_instance_settings
7
+ from .core.cloud_sqlite_locker import clear_locker
8
+
9
+
10
+ def disconnect(mute: bool = False) -> None:
11
+ """Clear default instance configuration.
12
+
13
+ Returns `None` if succeeds, otherwise an exception is raised.
14
+
15
+ See Also:
16
+ Clear default instance configuration via the CLI, see `here <https://docs.lamin.ai/cli#disconnect>`__.
17
+ """
18
+ # settings._instance_exists can be true due to connect even without having a file
19
+ if settings._instance_exists:
20
+ instance = settings.instance
21
+ try:
22
+ instance._update_cloud_sqlite_file()
23
+ except Exception as e:
24
+ if isinstance(e, FileNotFoundError):
25
+ logger.warning("did not find local cache file")
26
+ elif isinstance(e, PermissionError):
27
+ logger.warning("did not upload cache file - not enough permissions")
28
+ else:
29
+ raise e
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
35
+ if not mute:
36
+ logger.success(f"disconnected instance: {instance.slug}")
37
+ elif not mute:
38
+ logger.info("no instance loaded")
lamindb_setup/_django.py CHANGED
@@ -1,39 +1,39 @@
1
- from __future__ import annotations
2
-
3
- from .core._settings import settings
4
- from .core.django import setup_django
5
-
6
-
7
- def django(command: str, package_name: str | None = None, **kwargs):
8
- r"""Call Django commands.
9
-
10
- Examples:
11
-
12
- Reset auto-incrementing primary integer ids after a database import:
13
-
14
- >>> import lamindb as ln
15
- >>> ln.setup.django("sqlsequencereset", "lamindb")
16
- BEGIN;
17
- SELECT setval(pg_get_serial_sequence('"lamindb_user"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lamindb_user"; # noqa
18
- SELECT setval(pg_get_serial_sequence('"lamindb_storage"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lamindb_storage"; # noqa
19
- COMMIT;
20
-
21
- You can then run the SQL output that you'll see like so:
22
-
23
- >>> sql = \"\"\"BEGIN;
24
- SELECT setval(pg_get_serial_sequence('"lamindb_user"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lamindb_user"; # noqa
25
- SELECT setval(pg_get_serial_sequence('"lamindb_storage"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lamindb_storage"; # noqa
26
- COMMIT;\"\"\"
27
- >>> from django.db import connection
28
- >>> with connection.cursor() as cursor:
29
- cursor.execute(sql)
30
-
31
- """
32
- from django.core.management import call_command
33
-
34
- setup_django(settings.instance)
35
- if package_name is not None:
36
- args = [package_name]
37
- else:
38
- args = []
39
- call_command(command, *args, **kwargs)
1
+ from __future__ import annotations
2
+
3
+ from .core._settings import settings
4
+ from .core.django import setup_django
5
+
6
+
7
+ def django(command: str, package_name: str | None = None, **kwargs):
8
+ r"""Call Django commands.
9
+
10
+ Examples:
11
+
12
+ Reset auto-incrementing primary integer ids after a database import:
13
+
14
+ >>> import lamindb as ln
15
+ >>> ln.setup.django("sqlsequencereset", "lamindb")
16
+ BEGIN;
17
+ SELECT setval(pg_get_serial_sequence('"lamindb_user"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lamindb_user"; # noqa
18
+ SELECT setval(pg_get_serial_sequence('"lamindb_storage"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lamindb_storage"; # noqa
19
+ COMMIT;
20
+
21
+ You can then run the SQL output that you'll see like so:
22
+
23
+ >>> sql = \"\"\"BEGIN;
24
+ SELECT setval(pg_get_serial_sequence('"lamindb_user"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lamindb_user"; # noqa
25
+ SELECT setval(pg_get_serial_sequence('"lamindb_storage"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lamindb_storage"; # noqa
26
+ COMMIT;\"\"\"
27
+ >>> from django.db import connection
28
+ >>> with connection.cursor() as cursor:
29
+ cursor.execute(sql)
30
+
31
+ """
32
+ from django.core.management import call_command
33
+
34
+ setup_django(settings.instance)
35
+ if package_name is not None:
36
+ args = [package_name]
37
+ else:
38
+ args = []
39
+ call_command(command, *args, **kwargs)
@@ -1,19 +1,19 @@
1
- import sys
2
- import warnings
3
- from importlib.metadata import entry_points
4
-
5
-
6
- def call_registered_entry_points(group, **kwargs):
7
- """load and call entry points registered under group."""
8
- eps = entry_points(group=group)
9
-
10
- for ep in eps:
11
- func = ep.load()
12
- try:
13
- func(**kwargs)
14
- except BaseException as e:
15
- warnings.warn(
16
- f"Error loading entry point of group {group!r}: {ep} -> {e}",
17
- RuntimeWarning,
18
- stacklevel=2,
19
- )
1
+ import sys
2
+ import warnings
3
+ from importlib.metadata import entry_points
4
+
5
+
6
+ def call_registered_entry_points(group, **kwargs):
7
+ """load and call entry points registered under group."""
8
+ eps = entry_points(group=group)
9
+
10
+ for ep in eps:
11
+ func = ep.load()
12
+ try:
13
+ func(**kwargs)
14
+ except BaseException as e:
15
+ warnings.warn(
16
+ f"Error loading entry point of group {group!r}: {ep} -> {e}",
17
+ RuntimeWarning,
18
+ stacklevel=2,
19
+ )