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.
- lamindb_setup/__init__.py +74 -0
- lamindb_setup/_cache.py +48 -0
- lamindb_setup/_check.py +7 -0
- lamindb_setup/_check_setup.py +92 -0
- lamindb_setup/_close.py +35 -0
- lamindb_setup/_connect_instance.py +429 -0
- lamindb_setup/_delete.py +141 -0
- lamindb_setup/_django.py +39 -0
- lamindb_setup/_entry_points.py +22 -0
- lamindb_setup/_exportdb.py +68 -0
- lamindb_setup/_importdb.py +50 -0
- lamindb_setup/_init_instance.py +411 -0
- lamindb_setup/_migrate.py +239 -0
- lamindb_setup/_register_instance.py +36 -0
- lamindb_setup/_schema.py +27 -0
- lamindb_setup/_schema_metadata.py +411 -0
- lamindb_setup/_set_managed_storage.py +55 -0
- lamindb_setup/_setup_user.py +137 -0
- lamindb_setup/_silence_loggers.py +44 -0
- lamindb_setup/core/__init__.py +21 -0
- lamindb_setup/core/_aws_credentials.py +151 -0
- lamindb_setup/core/_aws_storage.py +48 -0
- lamindb_setup/core/_deprecated.py +55 -0
- lamindb_setup/core/_docs.py +14 -0
- lamindb_setup/core/_hub_client.py +173 -0
- lamindb_setup/core/_hub_core.py +554 -0
- lamindb_setup/core/_hub_crud.py +211 -0
- lamindb_setup/core/_hub_utils.py +109 -0
- lamindb_setup/core/_private_django_api.py +88 -0
- lamindb_setup/core/_settings.py +184 -0
- lamindb_setup/core/_settings_instance.py +485 -0
- lamindb_setup/core/_settings_load.py +117 -0
- lamindb_setup/core/_settings_save.py +92 -0
- lamindb_setup/core/_settings_storage.py +350 -0
- lamindb_setup/core/_settings_store.py +75 -0
- lamindb_setup/core/_settings_user.py +55 -0
- lamindb_setup/core/_setup_bionty_sources.py +101 -0
- lamindb_setup/core/cloud_sqlite_locker.py +237 -0
- lamindb_setup/core/django.py +115 -0
- lamindb_setup/core/exceptions.py +10 -0
- lamindb_setup/core/hashing.py +116 -0
- lamindb_setup/core/types.py +17 -0
- lamindb_setup/core/upath.py +779 -0
- lamindb_setup-0.78.0.dist-info/LICENSE +201 -0
- lamindb_setup-0.78.0.dist-info/METADATA +47 -0
- lamindb_setup-0.78.0.dist-info/RECORD +47 -0
- lamindb_setup-0.78.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
from uuid import UUID, uuid4
|
|
5
|
+
|
|
6
|
+
from lamin_utils import logger
|
|
7
|
+
from supabase.client import Client # noqa
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def select_instance_by_owner_name(
|
|
11
|
+
owner: str,
|
|
12
|
+
name: str,
|
|
13
|
+
client: Client,
|
|
14
|
+
) -> dict | None:
|
|
15
|
+
try:
|
|
16
|
+
data = (
|
|
17
|
+
client.table("instance")
|
|
18
|
+
.select(
|
|
19
|
+
"*, account!inner!instance_account_id_28936e8f_fk_account_id(*),"
|
|
20
|
+
" storage!inner!storage_instance_id_359fca71_fk_instance_id(*)"
|
|
21
|
+
)
|
|
22
|
+
.eq("name", name)
|
|
23
|
+
.eq("account.handle", owner)
|
|
24
|
+
.eq("storage.is_default", True)
|
|
25
|
+
.execute()
|
|
26
|
+
.data
|
|
27
|
+
)
|
|
28
|
+
except Exception:
|
|
29
|
+
return None
|
|
30
|
+
if len(data) == 0:
|
|
31
|
+
return None
|
|
32
|
+
result = data[0]
|
|
33
|
+
# this is now a list
|
|
34
|
+
# assume only one default storage
|
|
35
|
+
result["storage"] = result["storage"][0]
|
|
36
|
+
return result
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# --------------- ACCOUNT ----------------------
|
|
40
|
+
def select_account_by_handle(
|
|
41
|
+
handle: str,
|
|
42
|
+
client: Client,
|
|
43
|
+
):
|
|
44
|
+
data = client.table("account").select("*").eq("handle", handle).execute().data
|
|
45
|
+
if len(data) == 0:
|
|
46
|
+
return None
|
|
47
|
+
return data[0]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def select_account_handle_name_by_lnid(lnid: str, client: Client):
|
|
51
|
+
data = (
|
|
52
|
+
client.table("account").select("handle, name").eq("lnid", lnid).execute().data
|
|
53
|
+
)
|
|
54
|
+
if not data:
|
|
55
|
+
return None
|
|
56
|
+
return data[0]
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# --------------- INSTANCE ----------------------
|
|
60
|
+
def select_instance_by_name(
|
|
61
|
+
account_id: str,
|
|
62
|
+
name: str,
|
|
63
|
+
client: Client,
|
|
64
|
+
):
|
|
65
|
+
data = (
|
|
66
|
+
client.table("instance")
|
|
67
|
+
.select("*")
|
|
68
|
+
.eq("account_id", account_id)
|
|
69
|
+
.eq("name", name)
|
|
70
|
+
.execute()
|
|
71
|
+
.data
|
|
72
|
+
)
|
|
73
|
+
if len(data) == 0:
|
|
74
|
+
return None
|
|
75
|
+
return data[0]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def select_instance_by_id(
|
|
79
|
+
instance_id: str,
|
|
80
|
+
client: Client,
|
|
81
|
+
):
|
|
82
|
+
response = client.table("instance").select("*").eq("id", instance_id).execute()
|
|
83
|
+
if len(response.data) == 0:
|
|
84
|
+
return None
|
|
85
|
+
return response.data[0]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def select_instance_by_id_with_storage(
|
|
89
|
+
instance_id: str,
|
|
90
|
+
client: Client,
|
|
91
|
+
):
|
|
92
|
+
response = (
|
|
93
|
+
client.table("instance")
|
|
94
|
+
.select("*, storage!instance_storage_id_87963cc8_fk_storage_id(*)")
|
|
95
|
+
.eq("id", instance_id)
|
|
96
|
+
.execute()
|
|
97
|
+
)
|
|
98
|
+
if len(response.data) == 0:
|
|
99
|
+
return None
|
|
100
|
+
return response.data[0]
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def update_instance(instance_id: str, instance_fields: dict, client: Client):
|
|
104
|
+
response = (
|
|
105
|
+
client.table("instance").update(instance_fields).eq("id", instance_id).execute()
|
|
106
|
+
)
|
|
107
|
+
if len(response.data) == 0:
|
|
108
|
+
raise PermissionError(
|
|
109
|
+
f"Update of instance with {instance_id} was not successful. Probably, you"
|
|
110
|
+
" don't have sufficient permissions."
|
|
111
|
+
)
|
|
112
|
+
return response.data[0]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# --------------- COLLABORATOR ----------------------
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def select_collaborator(
|
|
119
|
+
instance_id: str,
|
|
120
|
+
account_id: str,
|
|
121
|
+
client: Client,
|
|
122
|
+
):
|
|
123
|
+
data = (
|
|
124
|
+
client.table("account_instance")
|
|
125
|
+
.select("*")
|
|
126
|
+
.eq("instance_id", instance_id)
|
|
127
|
+
.eq("account_id", account_id)
|
|
128
|
+
.execute()
|
|
129
|
+
.data
|
|
130
|
+
)
|
|
131
|
+
if len(data) == 0:
|
|
132
|
+
return None
|
|
133
|
+
return data[0]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# --------------- STORAGE ----------------------
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def select_default_storage_by_instance_id(
|
|
140
|
+
instance_id: str, client: Client
|
|
141
|
+
) -> dict | None:
|
|
142
|
+
try:
|
|
143
|
+
data = (
|
|
144
|
+
client.table("storage")
|
|
145
|
+
.select("*")
|
|
146
|
+
.eq("instance_id", instance_id)
|
|
147
|
+
.eq("is_default", True)
|
|
148
|
+
.execute()
|
|
149
|
+
.data
|
|
150
|
+
)
|
|
151
|
+
except Exception:
|
|
152
|
+
return None
|
|
153
|
+
if len(data) == 0:
|
|
154
|
+
return None
|
|
155
|
+
return data[0]
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# --------------- DBUser ----------------------
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def insert_db_user(
|
|
162
|
+
*,
|
|
163
|
+
name: str,
|
|
164
|
+
db_user_name: str,
|
|
165
|
+
db_user_password: str,
|
|
166
|
+
instance_id: UUID,
|
|
167
|
+
client: Client,
|
|
168
|
+
) -> None:
|
|
169
|
+
fields = (
|
|
170
|
+
{
|
|
171
|
+
"id": uuid4().hex,
|
|
172
|
+
"instance_id": instance_id.hex,
|
|
173
|
+
"name": name,
|
|
174
|
+
"db_user_name": db_user_name,
|
|
175
|
+
"db_user_password": db_user_password,
|
|
176
|
+
},
|
|
177
|
+
)
|
|
178
|
+
data = client.table("db_user").insert(fields).execute().data
|
|
179
|
+
return data[0]
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def select_db_user_by_instance(instance_id: str, client: Client):
|
|
183
|
+
"""Get db_user for which client has permission."""
|
|
184
|
+
data = (
|
|
185
|
+
client.table("db_user")
|
|
186
|
+
.select("*")
|
|
187
|
+
.eq("instance_id", instance_id)
|
|
188
|
+
.execute()
|
|
189
|
+
.data
|
|
190
|
+
)
|
|
191
|
+
if len(data) == 0:
|
|
192
|
+
return None
|
|
193
|
+
elif len(data) > 1:
|
|
194
|
+
for item in data:
|
|
195
|
+
if item["name"] == "write":
|
|
196
|
+
return item
|
|
197
|
+
logger.warning("found multiple db credentials, using the first one")
|
|
198
|
+
return data[0]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _delete_instance_record(instance_id: UUID, client: Client) -> None:
|
|
202
|
+
if not isinstance(instance_id, UUID):
|
|
203
|
+
instance_id = UUID(instance_id)
|
|
204
|
+
response = client.table("instance").delete().eq("id", instance_id.hex).execute()
|
|
205
|
+
if response.data:
|
|
206
|
+
logger.important(f"deleted instance record on hub {instance_id.hex}")
|
|
207
|
+
else:
|
|
208
|
+
raise PermissionError(
|
|
209
|
+
f"Deleting of instance with {instance_id.hex} was not successful. Probably, you"
|
|
210
|
+
" don't have sufficient permissions."
|
|
211
|
+
)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, ClassVar
|
|
4
|
+
from urllib.parse import urlparse, urlunparse
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field, GetCoreSchemaHandler
|
|
7
|
+
from pydantic_core import CoreSchema, core_schema
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def validate_schema_arg(schema: str | None = None) -> str:
|
|
11
|
+
if schema is None or schema == "":
|
|
12
|
+
return ""
|
|
13
|
+
# currently no actual validation, can add back if we see a need
|
|
14
|
+
# the following just strips white spaces
|
|
15
|
+
to_be_validated = [s.strip() for s in schema.split(",")]
|
|
16
|
+
return ",".join(to_be_validated)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def validate_db_arg(db: str | None) -> None:
|
|
20
|
+
if db is not None:
|
|
21
|
+
LaminDsnModel(db=db)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LaminDsn(str):
|
|
25
|
+
allowed_schemes: ClassVar[set[str]] = {
|
|
26
|
+
"postgresql",
|
|
27
|
+
# future enabled schemes
|
|
28
|
+
# "snowflake",
|
|
29
|
+
# "bigquery"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def __get_pydantic_core_schema__(
|
|
34
|
+
cls, source_type: Any, handler: GetCoreSchemaHandler
|
|
35
|
+
) -> CoreSchema:
|
|
36
|
+
return core_schema.no_info_after_validator_function(
|
|
37
|
+
cls.validate,
|
|
38
|
+
core_schema.str_schema(),
|
|
39
|
+
serialization=core_schema.plain_serializer_function_ser_schema(str),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def validate(cls, v: Any) -> LaminDsn:
|
|
44
|
+
if isinstance(v, str):
|
|
45
|
+
parsed = urlparse(v)
|
|
46
|
+
if parsed.scheme not in cls.allowed_schemes:
|
|
47
|
+
raise ValueError(f"Invalid scheme: {parsed.scheme}")
|
|
48
|
+
return cls(v)
|
|
49
|
+
elif isinstance(v, cls):
|
|
50
|
+
return v
|
|
51
|
+
else:
|
|
52
|
+
raise ValueError(f"Invalid value for LaminDsn: {v}")
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def user(self) -> str | None:
|
|
56
|
+
return urlparse(self).username
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def password(self) -> str | None:
|
|
60
|
+
return urlparse(self).password
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def host(self) -> str | None:
|
|
64
|
+
return urlparse(self).hostname
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def port(self) -> int | None:
|
|
68
|
+
return urlparse(self).port
|
|
69
|
+
|
|
70
|
+
@property
|
|
71
|
+
def database(self) -> str:
|
|
72
|
+
return urlparse(self).path.lstrip("/")
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def scheme(self) -> str:
|
|
76
|
+
return urlparse(self).scheme
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def build(
|
|
80
|
+
cls,
|
|
81
|
+
*,
|
|
82
|
+
scheme: str,
|
|
83
|
+
user: str | None = None,
|
|
84
|
+
password: str | None = None,
|
|
85
|
+
host: str,
|
|
86
|
+
port: int | None = None,
|
|
87
|
+
database: str | None = None,
|
|
88
|
+
query: str | None = None,
|
|
89
|
+
fragment: str | None = None,
|
|
90
|
+
) -> LaminDsn:
|
|
91
|
+
netloc = host
|
|
92
|
+
if port is not None:
|
|
93
|
+
netloc = f"{netloc}:{port}"
|
|
94
|
+
if user is not None:
|
|
95
|
+
auth = user
|
|
96
|
+
if password is not None:
|
|
97
|
+
auth = f"{auth}:{password}"
|
|
98
|
+
netloc = f"{auth}@{netloc}"
|
|
99
|
+
|
|
100
|
+
path = f"/{database}" if database else ""
|
|
101
|
+
|
|
102
|
+
url = urlunparse((scheme, netloc, path, "", query or "", fragment or ""))
|
|
103
|
+
return cls(url)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class LaminDsnModel(BaseModel):
|
|
107
|
+
db: LaminDsn = Field(..., description="The database DSN")
|
|
108
|
+
|
|
109
|
+
model_config = {"arbitrary_types_allowed": True}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def find_vscode_stubs_folder() -> Path | None:
|
|
8
|
+
# Possible locations of VSCode extensions
|
|
9
|
+
possible_locations = [
|
|
10
|
+
Path.home() / ".vscode" / "extensions", # Linux and macOS
|
|
11
|
+
Path.home() / ".vscode-server" / "extensions", # Remote development
|
|
12
|
+
Path(os.environ.get("APPDATA", "")) / "Code" / "User" / "extensions", # Windows
|
|
13
|
+
Path("/usr/share/code/resources/app/extensions"), # Some Linux distributions
|
|
14
|
+
]
|
|
15
|
+
for location in possible_locations:
|
|
16
|
+
if location.exists():
|
|
17
|
+
# Look for Pylance extension folder
|
|
18
|
+
pylance_folders = list(location.glob("ms-python.vscode-pylance-*"))
|
|
19
|
+
if pylance_folders:
|
|
20
|
+
# Sort to get the latest version
|
|
21
|
+
latest_pylance = sorted(pylance_folders)[-1]
|
|
22
|
+
stubs_folder = (
|
|
23
|
+
latest_pylance / "dist" / "bundled" / "stubs" / "django-stubs"
|
|
24
|
+
)
|
|
25
|
+
if stubs_folder.exists():
|
|
26
|
+
return stubs_folder
|
|
27
|
+
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def private_django_api(reverse=False):
|
|
32
|
+
from django import db
|
|
33
|
+
|
|
34
|
+
# the order here matters
|
|
35
|
+
# changing it might break the tests
|
|
36
|
+
attributes = [
|
|
37
|
+
"DoesNotExist",
|
|
38
|
+
"MultipleObjectsReturned",
|
|
39
|
+
"add_to_class",
|
|
40
|
+
"adelete",
|
|
41
|
+
"refresh_from_db",
|
|
42
|
+
"asave",
|
|
43
|
+
"clean",
|
|
44
|
+
"clean_fields",
|
|
45
|
+
"date_error_message",
|
|
46
|
+
"get_constraints",
|
|
47
|
+
"get_deferred_fields",
|
|
48
|
+
"prepare_database_save",
|
|
49
|
+
"save_base",
|
|
50
|
+
"serializable_value",
|
|
51
|
+
"unique_error_message",
|
|
52
|
+
"validate_constraints",
|
|
53
|
+
"validate_unique",
|
|
54
|
+
]
|
|
55
|
+
if reverse:
|
|
56
|
+
attributes.append("arefresh_from_db")
|
|
57
|
+
attributes.append("full_clean")
|
|
58
|
+
else:
|
|
59
|
+
attributes.append("a_refresh_from_db")
|
|
60
|
+
attributes.append("full__clean")
|
|
61
|
+
|
|
62
|
+
django_path = Path(db.__file__).parent.parent
|
|
63
|
+
|
|
64
|
+
encoding = "utf8" if os.name == "nt" else None
|
|
65
|
+
|
|
66
|
+
def prune_file(file_path):
|
|
67
|
+
content = file_path.read_text(encoding=encoding)
|
|
68
|
+
original_content = content
|
|
69
|
+
|
|
70
|
+
for attr in attributes:
|
|
71
|
+
old_name = f"_{attr}" if reverse else attr
|
|
72
|
+
new_name = attr if reverse else f"_{attr}"
|
|
73
|
+
content = content.replace(old_name, new_name)
|
|
74
|
+
|
|
75
|
+
if not reverse:
|
|
76
|
+
content = content.replace("Field_DoesNotExist", "FieldDoesNotExist")
|
|
77
|
+
content = content.replace("Object_DoesNotExist", "ObjectDoesNotExist")
|
|
78
|
+
|
|
79
|
+
if content != original_content:
|
|
80
|
+
file_path.write_text(content, encoding=encoding)
|
|
81
|
+
|
|
82
|
+
for file_path in django_path.rglob("*.py"):
|
|
83
|
+
prune_file(file_path)
|
|
84
|
+
|
|
85
|
+
pylance_path = find_vscode_stubs_folder()
|
|
86
|
+
if pylance_path is not None:
|
|
87
|
+
for file_path in pylance_path.rglob("*.pyi"):
|
|
88
|
+
prune_file(file_path)
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from appdirs import AppDirs
|
|
7
|
+
|
|
8
|
+
from ._settings_load import (
|
|
9
|
+
load_instance_settings,
|
|
10
|
+
load_or_create_user_settings,
|
|
11
|
+
load_system_storage_settings,
|
|
12
|
+
)
|
|
13
|
+
from ._settings_store import current_instance_settings_file, settings_dir
|
|
14
|
+
from .upath import LocalPathClasses, UPath
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from lamindb_setup.core import InstanceSettings, StorageSettings, UserSettings
|
|
20
|
+
|
|
21
|
+
from .types import UPathStr
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
DEFAULT_CACHE_DIR = UPath(AppDirs("lamindb", "laminlabs").user_cache_dir)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _process_cache_path(cache_path: UPathStr | None):
|
|
28
|
+
if cache_path is None or cache_path == "null":
|
|
29
|
+
return None
|
|
30
|
+
cache_dir = UPath(cache_path)
|
|
31
|
+
if not isinstance(cache_dir, LocalPathClasses):
|
|
32
|
+
raise ValueError("cache dir should be a local path.")
|
|
33
|
+
if cache_dir.exists() and not cache_dir.is_dir():
|
|
34
|
+
raise ValueError("cache dir should be a directory.")
|
|
35
|
+
return cache_dir
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class SetupSettings:
|
|
39
|
+
"""Setup settings."""
|
|
40
|
+
|
|
41
|
+
_using_key: str | None = None # set through lamindb.settings
|
|
42
|
+
|
|
43
|
+
_user_settings: UserSettings | None = None
|
|
44
|
+
_instance_settings: InstanceSettings | None = None
|
|
45
|
+
|
|
46
|
+
_user_settings_env: str | None = None
|
|
47
|
+
_instance_settings_env: str | None = None
|
|
48
|
+
|
|
49
|
+
_auto_connect_path: Path = settings_dir / "auto_connect"
|
|
50
|
+
_private_django_api_path: Path = settings_dir / "private_django_api"
|
|
51
|
+
|
|
52
|
+
_cache_dir: Path | None = None
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def _instance_settings_path(self) -> Path:
|
|
56
|
+
return current_instance_settings_file()
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def settings_dir(self) -> Path:
|
|
60
|
+
"""The directory that holds locally persisted settings."""
|
|
61
|
+
return settings_dir
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def auto_connect(self) -> bool:
|
|
65
|
+
"""Auto-connect to current instance upon `import lamindb`.
|
|
66
|
+
|
|
67
|
+
Upon installing `lamindb`, this setting is `False`.
|
|
68
|
+
|
|
69
|
+
Upon calling `lamin init` or `lamin connect` on the CLI, this setting is switched to `True`.
|
|
70
|
+
|
|
71
|
+
`ln.connect()` doesn't change the value of this setting.
|
|
72
|
+
|
|
73
|
+
You can manually change this setting
|
|
74
|
+
|
|
75
|
+
- in Python: `ln.setup.settings.auto_connect = True/False`
|
|
76
|
+
- via the CLI: `lamin settings set auto-connect true/false`
|
|
77
|
+
"""
|
|
78
|
+
return self._auto_connect_path.exists()
|
|
79
|
+
|
|
80
|
+
@auto_connect.setter
|
|
81
|
+
def auto_connect(self, value: bool) -> None:
|
|
82
|
+
if value:
|
|
83
|
+
self._auto_connect_path.touch()
|
|
84
|
+
else:
|
|
85
|
+
self._auto_connect_path.unlink(missing_ok=True)
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def private_django_api(self) -> bool:
|
|
89
|
+
"""Turn internal Django API private to clean up the API (default `False`).
|
|
90
|
+
|
|
91
|
+
This patches your local pip-installed django installation. You can undo
|
|
92
|
+
the patch by setting this back to `False`.
|
|
93
|
+
"""
|
|
94
|
+
return self._private_django_api_path.exists()
|
|
95
|
+
|
|
96
|
+
@private_django_api.setter
|
|
97
|
+
def private_django_api(self, value: bool) -> None:
|
|
98
|
+
from ._private_django_api import private_django_api
|
|
99
|
+
|
|
100
|
+
# we don't want to call private_django_api() twice
|
|
101
|
+
if value and not self.private_django_api:
|
|
102
|
+
private_django_api()
|
|
103
|
+
self._private_django_api_path.touch()
|
|
104
|
+
elif not value and self.private_django_api:
|
|
105
|
+
private_django_api(reverse=True)
|
|
106
|
+
self._private_django_api_path.unlink(missing_ok=True)
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def user(self) -> UserSettings:
|
|
110
|
+
"""Settings of current user."""
|
|
111
|
+
env_changed = (
|
|
112
|
+
self._user_settings_env is not None
|
|
113
|
+
and self._user_settings_env != get_env_name()
|
|
114
|
+
)
|
|
115
|
+
if self._user_settings is None or env_changed:
|
|
116
|
+
self._user_settings = load_or_create_user_settings()
|
|
117
|
+
self._user_settings_env = get_env_name()
|
|
118
|
+
if self._user_settings and self._user_settings.uid is None:
|
|
119
|
+
raise RuntimeError("Need to login, first: lamin login <email>")
|
|
120
|
+
return self._user_settings # type: ignore
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def instance(self) -> InstanceSettings:
|
|
124
|
+
"""Settings of current LaminDB instance."""
|
|
125
|
+
env_changed = (
|
|
126
|
+
self._instance_settings_env is not None
|
|
127
|
+
and self._instance_settings_env != get_env_name()
|
|
128
|
+
)
|
|
129
|
+
if self._instance_settings is None or env_changed:
|
|
130
|
+
self._instance_settings = load_instance_settings()
|
|
131
|
+
self._instance_settings_env = get_env_name()
|
|
132
|
+
return self._instance_settings # type: ignore
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def storage(self) -> StorageSettings:
|
|
136
|
+
"""Settings of default storage."""
|
|
137
|
+
return self.instance.storage
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def _instance_exists(self):
|
|
141
|
+
try:
|
|
142
|
+
self.instance # noqa
|
|
143
|
+
return True
|
|
144
|
+
# this is implicit logic that catches if no instance is loaded
|
|
145
|
+
except SystemExit:
|
|
146
|
+
return False
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def cache_dir(self) -> UPath:
|
|
150
|
+
"""Cache root, a local directory to cache cloud files."""
|
|
151
|
+
if "LAMIN_CACHE_DIR" in os.environ:
|
|
152
|
+
cache_dir = UPath(os.environ["LAMIN_CACHE_DIR"])
|
|
153
|
+
elif self._cache_dir is None:
|
|
154
|
+
cache_path = load_system_storage_settings().get("lamindb_cache_path", None)
|
|
155
|
+
cache_dir = _process_cache_path(cache_path)
|
|
156
|
+
if cache_dir is None:
|
|
157
|
+
cache_dir = DEFAULT_CACHE_DIR
|
|
158
|
+
self._cache_dir = cache_dir
|
|
159
|
+
else:
|
|
160
|
+
cache_dir = self._cache_dir
|
|
161
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
162
|
+
return cache_dir
|
|
163
|
+
|
|
164
|
+
def __repr__(self) -> str:
|
|
165
|
+
"""Rich string representation."""
|
|
166
|
+
repr = self.user.__repr__()
|
|
167
|
+
repr += f"\nAuto-connect in Python: {self.auto_connect}\n"
|
|
168
|
+
repr += f"Private Django API: {self.private_django_api}\n"
|
|
169
|
+
repr += f"Cache directory: {self.cache_dir}\n"
|
|
170
|
+
if self._instance_exists:
|
|
171
|
+
repr += self.instance.__repr__()
|
|
172
|
+
else:
|
|
173
|
+
repr += "\nNo instance connected"
|
|
174
|
+
return repr
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def get_env_name():
|
|
178
|
+
if "LAMIN_ENV" in os.environ:
|
|
179
|
+
return os.environ["LAMIN_ENV"]
|
|
180
|
+
else:
|
|
181
|
+
return "prod"
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
settings = SetupSettings()
|