lamindb_setup 0.78.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.
Files changed (47) hide show
  1. lamindb_setup/__init__.py +74 -0
  2. lamindb_setup/_cache.py +48 -0
  3. lamindb_setup/_check.py +7 -0
  4. lamindb_setup/_check_setup.py +92 -0
  5. lamindb_setup/_close.py +35 -0
  6. lamindb_setup/_connect_instance.py +429 -0
  7. lamindb_setup/_delete.py +141 -0
  8. lamindb_setup/_django.py +39 -0
  9. lamindb_setup/_entry_points.py +22 -0
  10. lamindb_setup/_exportdb.py +68 -0
  11. lamindb_setup/_importdb.py +50 -0
  12. lamindb_setup/_init_instance.py +411 -0
  13. lamindb_setup/_migrate.py +239 -0
  14. lamindb_setup/_register_instance.py +36 -0
  15. lamindb_setup/_schema.py +27 -0
  16. lamindb_setup/_schema_metadata.py +411 -0
  17. lamindb_setup/_set_managed_storage.py +55 -0
  18. lamindb_setup/_setup_user.py +137 -0
  19. lamindb_setup/_silence_loggers.py +44 -0
  20. lamindb_setup/core/__init__.py +21 -0
  21. lamindb_setup/core/_aws_credentials.py +151 -0
  22. lamindb_setup/core/_aws_storage.py +48 -0
  23. lamindb_setup/core/_deprecated.py +55 -0
  24. lamindb_setup/core/_docs.py +14 -0
  25. lamindb_setup/core/_hub_client.py +173 -0
  26. lamindb_setup/core/_hub_core.py +554 -0
  27. lamindb_setup/core/_hub_crud.py +211 -0
  28. lamindb_setup/core/_hub_utils.py +109 -0
  29. lamindb_setup/core/_private_django_api.py +88 -0
  30. lamindb_setup/core/_settings.py +184 -0
  31. lamindb_setup/core/_settings_instance.py +485 -0
  32. lamindb_setup/core/_settings_load.py +117 -0
  33. lamindb_setup/core/_settings_save.py +92 -0
  34. lamindb_setup/core/_settings_storage.py +350 -0
  35. lamindb_setup/core/_settings_store.py +75 -0
  36. lamindb_setup/core/_settings_user.py +55 -0
  37. lamindb_setup/core/_setup_bionty_sources.py +101 -0
  38. lamindb_setup/core/cloud_sqlite_locker.py +237 -0
  39. lamindb_setup/core/django.py +115 -0
  40. lamindb_setup/core/exceptions.py +10 -0
  41. lamindb_setup/core/hashing.py +116 -0
  42. lamindb_setup/core/types.py +17 -0
  43. lamindb_setup/core/upath.py +779 -0
  44. lamindb_setup-0.78.0.dist-info/LICENSE +201 -0
  45. lamindb_setup-0.78.0.dist-info/METADATA +47 -0
  46. lamindb_setup-0.78.0.dist-info/RECORD +47 -0
  47. lamindb_setup-0.78.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,141 @@
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_credentials import HOSTED_BUCKETS
11
+ from .core._hub_core import delete_instance as delete_instance_on_hub
12
+ from .core._hub_core import get_storage_records_for_instance
13
+ from .core._settings import settings
14
+ from .core._settings_storage import StorageSettings
15
+ from .core.upath import LocalPathClasses, check_storage_is_empty
16
+
17
+ if TYPE_CHECKING:
18
+ from pathlib import Path
19
+
20
+ from .core._settings_instance import InstanceSettings
21
+
22
+
23
+ def delete_cache(isettings: InstanceSettings):
24
+ # avoid init of root
25
+ root = isettings.storage._root_init
26
+ if not isinstance(root, LocalPathClasses):
27
+ cache_dir = settings.cache_dir / root.path
28
+ if cache_dir.exists():
29
+ shutil.rmtree(cache_dir)
30
+
31
+
32
+ def delete_exclusion_dir(isettings: InstanceSettings) -> None:
33
+ exclusion_dir = isettings.storage.root / f".lamindb/_exclusion/{isettings._id.hex}"
34
+ if exclusion_dir.exists():
35
+ exclusion_dir.rmdir()
36
+
37
+
38
+ def delete_by_isettings(isettings: InstanceSettings) -> None:
39
+ settings_file = isettings._get_settings_file()
40
+ if settings_file.exists():
41
+ settings_file.unlink()
42
+ delete_cache(isettings)
43
+ if isettings.dialect == "sqlite":
44
+ try:
45
+ if isettings._sqlite_file.exists():
46
+ isettings._sqlite_file.unlink()
47
+ except PermissionError:
48
+ logger.warning(
49
+ "Did not have permission to delete SQLite file:"
50
+ f" {isettings._sqlite_file}"
51
+ )
52
+ pass
53
+ # unset the global instance settings
54
+ if settings._instance_exists and isettings.slug == settings.instance.slug:
55
+ if settings._instance_settings_path.exists():
56
+ settings._instance_settings_path.unlink()
57
+ settings._instance_settings = None
58
+
59
+
60
+ def delete(slug: str, force: bool = False, require_empty: bool = True) -> int | None:
61
+ """Delete a LaminDB instance.
62
+
63
+ Args:
64
+ slug: The instance slug `account_handle/instance_name` or URL.
65
+ If the instance is owned by you, it suffices to pass the instance name.
66
+ force: Whether to skip the confirmation prompt.
67
+ require_empty: Whether to check if the instance is empty before deleting.
68
+ """
69
+ owner, name = get_owner_name_from_identifier(slug)
70
+ isettings = _connect_instance(owner, name, raise_permission_error=False)
71
+ if isettings.dialect != "sqlite":
72
+ logger.warning(
73
+ f"delete() does not yet affect your Postgres database at {isettings.db}"
74
+ )
75
+ if isettings.is_on_hub and settings.user.handle == "anonymous":
76
+ logger.warning(
77
+ "won't delete the hub component of this instance because you're not logged in"
78
+ )
79
+ if not force:
80
+ valid_responses = ["y", "yes"]
81
+ user_input = (
82
+ input(f"Are you sure you want to delete instance {isettings.slug}? (y/n) ")
83
+ .strip()
84
+ .lower()
85
+ )
86
+ if user_input not in valid_responses:
87
+ return -1
88
+
89
+ # the actual deletion process begins here
90
+ if isettings.dialect == "sqlite" and isettings.is_remote:
91
+ # delete the exlusion dir first because it's hard to count its objects
92
+ delete_exclusion_dir(isettings)
93
+ if isettings.storage.type_is_cloud and isettings.storage.root_as_str.startswith(
94
+ HOSTED_BUCKETS
95
+ ):
96
+ if not require_empty:
97
+ logger.warning(
98
+ "hosted storage always has to be empty, ignoring `require_empty`"
99
+ )
100
+ require_empty = True
101
+ # first the default storage
102
+ n_objects = check_storage_is_empty(
103
+ isettings.storage.root,
104
+ raise_error=require_empty,
105
+ account_for_sqlite_file=isettings.dialect == "sqlite",
106
+ )
107
+ if isettings.storage._mark_storage_root.exists():
108
+ isettings.storage._mark_storage_root.unlink(
109
+ missing_ok=True # this is totally weird, but needed on Py3.11
110
+ )
111
+ # now everything that's on the hub
112
+ if settings.user.handle != "anonymous":
113
+ storage_records = get_storage_records_for_instance(isettings._id)
114
+ for storage_record in storage_records:
115
+ if storage_record["root"] == isettings.storage.root_as_str:
116
+ continue
117
+ ssettings = StorageSettings(storage_record["root"]) # type: ignore
118
+ check_storage_is_empty(
119
+ ssettings.root, # type: ignore
120
+ raise_error=require_empty,
121
+ )
122
+ if ssettings._mark_storage_root.exists():
123
+ ssettings._mark_storage_root.unlink(
124
+ missing_ok=True # this is totally weird, but needed on Py3.11
125
+ )
126
+ logger.info(f"deleting instance {isettings.slug}")
127
+ # below we can skip check_storage_is_empty() because we already called
128
+ # it above
129
+ if settings.user.handle != "anonymous" and isettings.is_on_hub:
130
+ # start with deleting things on the hub
131
+ # this will error if the user doesn't have permission
132
+ delete_instance_on_hub(isettings._id, require_empty=False)
133
+ delete_by_isettings(isettings)
134
+ # if .lndb file was delete, then we might count -1
135
+ if n_objects <= 0 and isettings.storage.type == "local":
136
+ # dir is only empty after sqlite file was delete via delete_by_isettings
137
+ if (isettings.storage.root / ".lamindb").exists():
138
+ (isettings.storage.root / ".lamindb").rmdir()
139
+ if isettings.storage.root.exists():
140
+ isettings.storage.root.rmdir()
141
+ return None
@@ -0,0 +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"""Manage migrations.
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", "lnschema_core")
16
+ BEGIN;
17
+ SELECT setval(pg_get_serial_sequence('"lnschema_core_user"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lnschema_core_user"; # noqa
18
+ SELECT setval(pg_get_serial_sequence('"lnschema_core_storage"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lnschema_core_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('"lnschema_core_user"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lnschema_core_user"; # noqa
25
+ SELECT setval(pg_get_serial_sequence('"lnschema_core_storage"','id'), coalesce(max("id"), 1), max("id") IS NOT null) FROM "lnschema_core_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)
@@ -0,0 +1,22 @@
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
+ if sys.version_info >= (3, 10):
9
+ eps = entry_points(group=group)
10
+ else:
11
+ eps = entry_points().get(group, [])
12
+
13
+ for ep in eps:
14
+ func = ep.load()
15
+ try:
16
+ func(**kwargs)
17
+ except BaseException as e:
18
+ warnings.warn(
19
+ f"Error loading entry point of group {group!r}: {ep} -> {e}",
20
+ RuntimeWarning,
21
+ stacklevel=2,
22
+ )
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib import import_module
4
+ from pathlib import Path
5
+
6
+ MODELS = {
7
+ "core": {
8
+ "Collection": False,
9
+ "Artifact": False,
10
+ "Transform": False,
11
+ "Run": True,
12
+ "User": False,
13
+ "Storage": False,
14
+ "Feature": False,
15
+ "FeatureSet": False,
16
+ "ULabel": False,
17
+ },
18
+ # "bionty": {
19
+ # "Organism": False,
20
+ # "Gene": False,
21
+ # "Protein": False,
22
+ # "CellMarker": False,
23
+ # "Tissue": False,
24
+ # "CellType": False,
25
+ # "Disease": False,
26
+ # "CellLine": False,
27
+ # "Phenotype": False,
28
+ # "Pathway": False,
29
+ # "ExperimentalFactor": False,
30
+ # "DevelopmentalStage": False,
31
+ # "Ethnicity": False,
32
+ # "Source": False,
33
+ # },
34
+ # "wetlab": {
35
+ # "ExperimentType": False,
36
+ # "Experiment": False,
37
+ # "Well": False,
38
+ # "TreatmentTarget": False,
39
+ # "Treatment": False,
40
+ # "Biosample": False,
41
+ # "Techsample": False,
42
+ # },
43
+ }
44
+
45
+
46
+ def exportdb() -> None:
47
+ directory = Path("./lamindb_export/")
48
+ directory.mkdir(parents=True, exist_ok=True)
49
+ import pandas as pd
50
+
51
+ import lamindb_setup as ln_setup
52
+
53
+ def export_registry(registry, directory):
54
+ table_name = registry._meta.db_table
55
+ df = pd.read_sql_table(table_name, ln_setup.settings.instance.db)
56
+ df.to_parquet(directory / f"{table_name}.parquet", compression=None)
57
+
58
+ # export data to parquet files
59
+ print(f"\nexporting data to parquet files in: {directory}\n")
60
+ for schema_name, models in MODELS.items():
61
+ for model_name in models.keys():
62
+ schema_module = import_module(f"lnschema_{schema_name}")
63
+ registry = getattr(schema_module, model_name)
64
+ export_registry(registry, directory)
65
+ many_to_many_names = [field.name for field in registry._meta.many_to_many]
66
+ for many_to_many_name in many_to_many_names:
67
+ link_orm = getattr(registry, many_to_many_name).through
68
+ export_registry(link_orm, directory)
@@ -0,0 +1,50 @@
1
+ from __future__ import annotations
2
+
3
+ from importlib import import_module
4
+ from pathlib import Path
5
+
6
+ from ._exportdb import MODELS
7
+
8
+
9
+ def import_registry(registry, directory, connection):
10
+ import pandas as pd
11
+
12
+ table_name = registry._meta.db_table
13
+ df = pd.read_parquet(directory / f"{table_name}.parquet")
14
+ old_foreign_key_columns = [
15
+ column for column in df.columns if column.endswith("_old")
16
+ ]
17
+ for column in old_foreign_key_columns:
18
+ df.drop(column, axis=1, inplace=True)
19
+ df.to_sql(table_name, connection, if_exists="append", index=False)
20
+
21
+
22
+ def importdb() -> None:
23
+ # import data from parquet files
24
+ directory = Path("./lamindb_export/")
25
+ if directory.exists():
26
+ response = input(
27
+ f"\n\nDo you want to import registries from here: {directory}? (y/n)\n"
28
+ )
29
+ if response != "y":
30
+ return None
31
+ from sqlalchemy import create_engine, text
32
+
33
+ import lamindb_setup as ln_setup
34
+
35
+ engine = create_engine(ln_setup.settings.instance.db, echo=False)
36
+ with engine.begin() as connection:
37
+ if ln_setup.settings.instance.dialect == "postgresql":
38
+ connection.execute(text("SET CONSTRAINTS ALL DEFERRED;"))
39
+ for schema_name, models in MODELS.items():
40
+ for model_name in models.keys():
41
+ print(model_name)
42
+ schema_module = import_module(f"lnschema_{schema_name}")
43
+ registry = getattr(schema_module, model_name)
44
+ import_registry(registry, directory, connection)
45
+ many_to_many_names = [
46
+ field.name for field in registry._meta.many_to_many
47
+ ]
48
+ for many_to_many_name in many_to_many_names:
49
+ link_orm = getattr(registry, many_to_many_name).through
50
+ import_registry(link_orm, directory, connection)