lamindb_setup 0.70.0__py2.py3-none-any.whl → 0.71.1__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.
- lamindb_setup/__init__.py +15 -15
- lamindb_setup/_cache.py +4 -1
- lamindb_setup/_check.py +3 -0
- lamindb_setup/_check_setup.py +13 -7
- lamindb_setup/_close.py +2 -0
- lamindb_setup/_connect_instance.py +47 -26
- lamindb_setup/_delete.py +72 -40
- lamindb_setup/_django.py +4 -1
- lamindb_setup/_exportdb.py +4 -2
- lamindb_setup/_importdb.py +5 -1
- lamindb_setup/_init_instance.py +61 -45
- lamindb_setup/_migrate.py +16 -13
- lamindb_setup/_register_instance.py +10 -3
- lamindb_setup/_schema.py +6 -3
- lamindb_setup/_set_managed_storage.py +37 -0
- lamindb_setup/_setup_user.py +7 -7
- lamindb_setup/_silence_loggers.py +4 -2
- lamindb_setup/core/__init__.py +4 -3
- lamindb_setup/core/_aws_storage.py +3 -0
- lamindb_setup/core/_deprecated.py +2 -7
- lamindb_setup/core/_docs.py +2 -0
- lamindb_setup/core/_hub_client.py +12 -10
- lamindb_setup/core/_hub_core.py +203 -88
- lamindb_setup/core/_hub_crud.py +21 -12
- lamindb_setup/core/_hub_utils.py +11 -8
- lamindb_setup/core/_settings.py +23 -26
- lamindb_setup/core/_settings_instance.py +149 -81
- lamindb_setup/core/_settings_load.py +13 -7
- lamindb_setup/core/_settings_save.py +13 -8
- lamindb_setup/core/_settings_storage.py +76 -42
- lamindb_setup/core/_settings_store.py +4 -2
- lamindb_setup/core/_settings_user.py +10 -6
- lamindb_setup/core/_setup_bionty_sources.py +9 -2
- lamindb_setup/core/cloud_sqlite_locker.py +13 -10
- lamindb_setup/core/django.py +3 -1
- lamindb_setup/core/exceptions.py +4 -2
- lamindb_setup/core/hashing.py +15 -5
- lamindb_setup/core/types.py +5 -2
- lamindb_setup/core/upath.py +191 -88
- {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.1.dist-info}/METADATA +6 -4
- lamindb_setup-0.71.1.dist-info/RECORD +43 -0
- lamindb_setup/_add_remote_storage.py +0 -50
- lamindb_setup-0.70.0.dist-info/RECORD +0 -43
- {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.1.dist-info}/LICENSE +0 -0
- {lamindb_setup-0.70.0.dist-info → lamindb_setup-0.71.1.dist-info}/WHEEL +0 -0
lamindb_setup/core/_hub_core.py
CHANGED
|
@@ -1,35 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
1
4
|
import os
|
|
2
|
-
from typing import Optional, Tuple, Union, Dict
|
|
3
5
|
import uuid
|
|
4
|
-
from postgrest.exceptions import APIError
|
|
5
|
-
from uuid import UUID
|
|
6
|
-
from lamin_utils import logger
|
|
7
|
-
from supabase import Client
|
|
8
|
-
import lamindb_setup
|
|
9
|
-
import json
|
|
10
6
|
from importlib import metadata
|
|
11
|
-
from
|
|
12
|
-
from
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
from uuid import UUID
|
|
13
9
|
|
|
10
|
+
from lamin_utils import logger
|
|
11
|
+
from postgrest.exceptions import APIError
|
|
14
12
|
|
|
15
13
|
from ._hub_client import (
|
|
16
|
-
connect_hub,
|
|
17
|
-
call_with_fallback_auth,
|
|
18
14
|
call_with_fallback,
|
|
15
|
+
call_with_fallback_auth,
|
|
16
|
+
connect_hub,
|
|
19
17
|
)
|
|
20
18
|
from ._hub_crud import (
|
|
21
|
-
|
|
22
|
-
select_instance_by_id_with_storage,
|
|
19
|
+
_delete_instance_record,
|
|
23
20
|
select_account_by_handle,
|
|
24
21
|
select_db_user_by_instance,
|
|
22
|
+
select_instance_by_id_with_storage,
|
|
25
23
|
select_instance_by_name,
|
|
24
|
+
select_instance_by_owner_name,
|
|
26
25
|
select_storage,
|
|
27
|
-
_delete_instance_record,
|
|
28
26
|
)
|
|
27
|
+
from ._hub_crud import update_instance as _update_instance_record
|
|
29
28
|
from ._hub_utils import (
|
|
30
29
|
LaminDsn,
|
|
31
30
|
LaminDsnModel,
|
|
32
31
|
)
|
|
32
|
+
from ._settings import settings
|
|
33
|
+
from ._settings_storage import StorageSettings, base62
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from supabase import Client # type: ignore
|
|
37
|
+
|
|
38
|
+
from ._settings_instance import InstanceSettings
|
|
33
39
|
|
|
34
40
|
|
|
35
41
|
def delete_storage_record(
|
|
@@ -44,88 +50,192 @@ def delete_storage_record(
|
|
|
44
50
|
def _delete_storage_record(storage_uuid: UUID, client: Client) -> None:
|
|
45
51
|
if storage_uuid is None:
|
|
46
52
|
return None
|
|
47
|
-
|
|
48
|
-
|
|
53
|
+
response = client.table("storage").delete().eq("id", storage_uuid.hex).execute()
|
|
54
|
+
if response.data:
|
|
55
|
+
logger.important(f"deleted storage record on hub {storage_uuid.hex}")
|
|
56
|
+
else:
|
|
57
|
+
raise PermissionError(
|
|
58
|
+
f"Deleting of storage with {storage_uuid.hex} was not successful. Probably, you"
|
|
59
|
+
" don't have sufficient permissions."
|
|
60
|
+
)
|
|
49
61
|
|
|
50
62
|
|
|
51
|
-
def update_instance_record(instance_uuid: UUID, fields:
|
|
52
|
-
|
|
63
|
+
def update_instance_record(instance_uuid: UUID, fields: dict) -> None:
|
|
64
|
+
return call_with_fallback_auth(
|
|
65
|
+
_update_instance_record, instance_id=instance_uuid.hex, instance_fields=fields
|
|
66
|
+
)
|
|
67
|
+
|
|
53
68
|
|
|
69
|
+
def get_storage_records_for_instance(
|
|
70
|
+
instance_id: UUID,
|
|
71
|
+
) -> list[dict[str, str | int]]:
|
|
54
72
|
return call_with_fallback_auth(
|
|
55
|
-
|
|
73
|
+
_get_storage_records_for_instance,
|
|
74
|
+
instance_id=instance_id,
|
|
56
75
|
)
|
|
57
76
|
|
|
58
77
|
|
|
78
|
+
def _get_storage_records_for_instance(
|
|
79
|
+
instance_id: UUID, client: Client
|
|
80
|
+
) -> list[dict[str, str | int]]:
|
|
81
|
+
response = (
|
|
82
|
+
client.table("storage").select("*").eq("instance_id", instance_id.hex).execute()
|
|
83
|
+
)
|
|
84
|
+
return response.data
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _select_storage(
|
|
88
|
+
ssettings: StorageSettings, update_uid: bool, client: Client
|
|
89
|
+
) -> bool:
|
|
90
|
+
root = ssettings.root_as_str
|
|
91
|
+
response = client.table("storage").select("*").eq("root", root).execute()
|
|
92
|
+
if not response.data:
|
|
93
|
+
return False
|
|
94
|
+
else:
|
|
95
|
+
existing_storage = response.data[0]
|
|
96
|
+
if ssettings._instance_id is not None:
|
|
97
|
+
# consider storage settings that are meant to be managed by an instance
|
|
98
|
+
if UUID(existing_storage["instance_id"]) != ssettings._instance_id:
|
|
99
|
+
# everything is alright if the instance_id matches
|
|
100
|
+
# we're probably just switching storage locations
|
|
101
|
+
# below can be turned into a warning and then delegate the error
|
|
102
|
+
# to a unique constraint violation below
|
|
103
|
+
raise ValueError(
|
|
104
|
+
f"Storage root {root} is already managed by instance {existing_storage['instance_id']}."
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
# if the request is agnostic of the instance, that's alright,
|
|
108
|
+
# we'll update the instance_id with what's stored in the hub
|
|
109
|
+
ssettings._instance_id = UUID(existing_storage["instance_id"])
|
|
110
|
+
ssettings._uuid_ = UUID(existing_storage["id"])
|
|
111
|
+
if update_uid:
|
|
112
|
+
ssettings._uid = existing_storage["lnid"]
|
|
113
|
+
else:
|
|
114
|
+
assert ssettings._uid == existing_storage["lnid"]
|
|
115
|
+
return True
|
|
116
|
+
|
|
117
|
+
|
|
59
118
|
def init_storage(
|
|
60
119
|
ssettings: StorageSettings,
|
|
61
|
-
) ->
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
120
|
+
) -> None:
|
|
121
|
+
if settings.user.handle != "anonymous":
|
|
122
|
+
return call_with_fallback_auth(
|
|
123
|
+
_init_storage,
|
|
124
|
+
ssettings=ssettings,
|
|
125
|
+
)
|
|
126
|
+
else:
|
|
127
|
+
storage_exists = call_with_fallback(
|
|
128
|
+
_select_storage, ssettings=ssettings, update_uid=True
|
|
129
|
+
)
|
|
130
|
+
if storage_exists:
|
|
131
|
+
return None
|
|
132
|
+
else:
|
|
133
|
+
raise ValueError("Log in to create a storage location on the hub.")
|
|
66
134
|
|
|
67
135
|
|
|
68
|
-
def _init_storage(ssettings: StorageSettings, client: Client) ->
|
|
136
|
+
def _init_storage(ssettings: StorageSettings, client: Client) -> None:
|
|
69
137
|
from lamindb_setup import settings
|
|
70
138
|
|
|
71
139
|
# storage roots are always stored without the trailing slash in the SQL
|
|
72
140
|
# database
|
|
73
141
|
root = ssettings.root_as_str
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
id=
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
142
|
+
if _select_storage(ssettings, update_uid=True, client=client):
|
|
143
|
+
return None
|
|
144
|
+
if ssettings.type_is_cloud:
|
|
145
|
+
id = uuid.uuid5(uuid.NAMESPACE_URL, root)
|
|
146
|
+
else:
|
|
147
|
+
id = uuid.uuid4()
|
|
148
|
+
if ssettings._instance_id is None:
|
|
149
|
+
logger.warning(
|
|
150
|
+
f"will manage storage location {ssettings.root_as_str} with instance {settings.instance.slug}"
|
|
151
|
+
)
|
|
152
|
+
ssettings._instance_id = settings.instance._id
|
|
153
|
+
fields = {
|
|
154
|
+
"id": id.hex,
|
|
155
|
+
"lnid": ssettings.uid,
|
|
156
|
+
"created_by": settings.user._uuid.hex, # type: ignore
|
|
157
|
+
"root": root,
|
|
158
|
+
"region": ssettings.region,
|
|
159
|
+
"type": ssettings.type,
|
|
160
|
+
"instance_id": ssettings._instance_id.hex,
|
|
161
|
+
# the empty string is important as we want the user flow to be through LaminHub
|
|
162
|
+
# if this errors with unique constraint error, the user has to update
|
|
163
|
+
# the description in LaminHub
|
|
164
|
+
"description": "",
|
|
165
|
+
}
|
|
166
|
+
# TODO: add error message for violated unique constraint
|
|
167
|
+
# on root & description
|
|
88
168
|
client.table("storage").upsert(fields).execute()
|
|
89
|
-
|
|
169
|
+
ssettings._uuid_ = id
|
|
170
|
+
return None
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def delete_instance(identifier: UUID | str, require_empty: bool = True) -> str | None:
|
|
174
|
+
return call_with_fallback_auth(
|
|
175
|
+
_delete_instance, identifier=identifier, require_empty=require_empty
|
|
176
|
+
)
|
|
90
177
|
|
|
91
178
|
|
|
92
|
-
def
|
|
179
|
+
def _delete_instance(
|
|
180
|
+
identifier: UUID | str, require_empty: bool, client: Client
|
|
181
|
+
) -> str | None:
|
|
93
182
|
"""Fully delete an instance in the hub.
|
|
94
183
|
|
|
95
184
|
This function deletes the relevant instance and storage records in the hub,
|
|
96
185
|
conditional on the emptiness of the storage location.
|
|
97
186
|
"""
|
|
98
|
-
from .upath import check_storage_is_empty, create_path
|
|
99
187
|
from ._settings_storage import mark_storage_root
|
|
188
|
+
from .upath import check_storage_is_empty, create_path
|
|
100
189
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
190
|
+
# the "/" check is for backward compatibility with the old identifier format
|
|
191
|
+
if isinstance(identifier, UUID) or "/" not in identifier:
|
|
192
|
+
if isinstance(identifier, UUID):
|
|
193
|
+
instance_id_str = identifier.hex
|
|
194
|
+
else:
|
|
195
|
+
instance_id_str = identifier
|
|
196
|
+
instance_with_storage = select_instance_by_id_with_storage(
|
|
197
|
+
instance_id=instance_id_str, client=client
|
|
105
198
|
)
|
|
106
199
|
else:
|
|
107
200
|
owner, name = identifier.split("/")
|
|
108
|
-
instance_with_storage =
|
|
109
|
-
|
|
110
|
-
owner=owner,
|
|
111
|
-
name=name,
|
|
201
|
+
instance_with_storage = select_instance_by_owner_name(
|
|
202
|
+
owner=owner, name=name, client=client
|
|
112
203
|
)
|
|
113
204
|
|
|
114
205
|
if instance_with_storage is None:
|
|
115
|
-
logger.
|
|
116
|
-
return
|
|
206
|
+
logger.important("not deleting instance from hub as instance not found there")
|
|
207
|
+
return "instance-not-found"
|
|
117
208
|
|
|
209
|
+
storage_records = _get_storage_records_for_instance(
|
|
210
|
+
UUID(instance_with_storage["id"]),
|
|
211
|
+
client,
|
|
212
|
+
)
|
|
118
213
|
if require_empty:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
214
|
+
for storage_record in storage_records:
|
|
215
|
+
account_for_sqlite_file = (
|
|
216
|
+
instance_with_storage["db_scheme"] is None
|
|
217
|
+
and instance_with_storage["storage"]["root"] == storage_record["root"]
|
|
218
|
+
)
|
|
219
|
+
root_string = storage_record["root"]
|
|
220
|
+
# gate storage and instance deletion on empty storage location for
|
|
221
|
+
if client.auth.get_session() is not None:
|
|
222
|
+
access_token = client.auth.get_session().access_token
|
|
223
|
+
else:
|
|
224
|
+
access_token = None
|
|
225
|
+
root_path = create_path(root_string, access_token)
|
|
226
|
+
mark_storage_root(
|
|
227
|
+
root_path,
|
|
228
|
+
storage_record["lnid"], # type: ignore
|
|
229
|
+
) # address permission error
|
|
230
|
+
check_storage_is_empty(
|
|
231
|
+
root_path, account_for_sqlite_file=account_for_sqlite_file
|
|
232
|
+
)
|
|
233
|
+
_update_instance_record(instance_with_storage["id"], {"storage_id": None}, client)
|
|
234
|
+
# first delete the storage records because we will turn instance_id on
|
|
235
|
+
# storage into a FK soon
|
|
236
|
+
for storage_record in storage_records:
|
|
237
|
+
_delete_storage_record(UUID(storage_record["id"]), client) # type: ignore
|
|
238
|
+
_delete_instance_record(UUID(instance_with_storage["id"]), client)
|
|
129
239
|
return None
|
|
130
240
|
|
|
131
241
|
|
|
@@ -149,25 +259,25 @@ def _init_instance(isettings: InstanceSettings, client: Client) -> None:
|
|
|
149
259
|
lamindb_version = metadata.version("lamindb")
|
|
150
260
|
except metadata.PackageNotFoundError:
|
|
151
261
|
lamindb_version = None
|
|
152
|
-
fields =
|
|
153
|
-
id
|
|
154
|
-
account_id
|
|
155
|
-
name
|
|
156
|
-
storage_id
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
262
|
+
fields = {
|
|
263
|
+
"id": isettings._id.hex,
|
|
264
|
+
"account_id": settings.user._uuid.hex, # type: ignore
|
|
265
|
+
"name": isettings.name,
|
|
266
|
+
"storage_id": isettings.storage._uuid.hex, # type: ignore
|
|
267
|
+
"lnid": isettings.uid,
|
|
268
|
+
"schema_str": isettings._schema_str,
|
|
269
|
+
"lamindb_version": lamindb_version,
|
|
270
|
+
"public": False,
|
|
271
|
+
}
|
|
161
272
|
if isettings.dialect != "sqlite":
|
|
162
273
|
db_dsn = LaminDsnModel(db=isettings.db)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
)
|
|
274
|
+
db_fields = {
|
|
275
|
+
"db_scheme": db_dsn.db.scheme,
|
|
276
|
+
"db_host": db_dsn.db.host,
|
|
277
|
+
"db_port": db_dsn.db.port,
|
|
278
|
+
"db_database": db_dsn.db.database,
|
|
279
|
+
}
|
|
280
|
+
fields.update(db_fields)
|
|
171
281
|
# I'd like the following to be an upsert, but this seems to violate RLS
|
|
172
282
|
# Similarly, if we don't specify `returning="minimal"`, we'll violate RLS
|
|
173
283
|
# we could make this idempotent by catching an error, but this seems dangerous
|
|
@@ -177,6 +287,9 @@ def _init_instance(isettings: InstanceSettings, client: Client) -> None:
|
|
|
177
287
|
except APIError as e:
|
|
178
288
|
logger.warning("instance likely already exists")
|
|
179
289
|
raise e
|
|
290
|
+
client.table("storage").update(
|
|
291
|
+
{"instance_id": isettings._id.hex, "is_default": True}
|
|
292
|
+
).eq("id", isettings.storage._uuid.hex).execute() # type: ignore
|
|
180
293
|
logger.save(f"browse to: https://lamin.ai/{isettings.owner}/{isettings.name}")
|
|
181
294
|
|
|
182
295
|
|
|
@@ -184,7 +297,7 @@ def connect_instance(
|
|
|
184
297
|
*,
|
|
185
298
|
owner: str, # account_handle
|
|
186
299
|
name: str, # instance_name
|
|
187
|
-
) ->
|
|
300
|
+
) -> tuple[dict, dict] | str:
|
|
188
301
|
from ._settings import settings
|
|
189
302
|
|
|
190
303
|
if settings.user.handle != "anonymous":
|
|
@@ -198,7 +311,7 @@ def _connect_instance(
|
|
|
198
311
|
owner: str, # account_handle
|
|
199
312
|
name: str, # instance_name
|
|
200
313
|
client: Client,
|
|
201
|
-
) ->
|
|
314
|
+
) -> tuple[dict, dict] | str:
|
|
202
315
|
instance_account_storage = select_instance_by_owner_name(owner, name, client)
|
|
203
316
|
if instance_account_storage is None:
|
|
204
317
|
# try the via single requests, will take more time
|
|
@@ -207,7 +320,7 @@ def _connect_instance(
|
|
|
207
320
|
return "account-not-exists"
|
|
208
321
|
instance = select_instance_by_name(account["id"], name, client)
|
|
209
322
|
if instance is None:
|
|
210
|
-
return "instance-not-
|
|
323
|
+
return "instance-not-found"
|
|
211
324
|
# get default storage
|
|
212
325
|
storage = select_storage(instance["storage_id"], client)
|
|
213
326
|
if storage is None:
|
|
@@ -238,7 +351,7 @@ def _connect_instance(
|
|
|
238
351
|
return instance, storage
|
|
239
352
|
|
|
240
353
|
|
|
241
|
-
def access_aws(storage_root: str, access_token:
|
|
354
|
+
def access_aws(storage_root: str, access_token: str | None = None) -> dict[str, str]:
|
|
242
355
|
from ._settings import settings
|
|
243
356
|
|
|
244
357
|
if settings.user.handle != "anonymous" or access_token is not None:
|
|
@@ -250,7 +363,9 @@ def access_aws(storage_root: str, access_token: Optional[str] = None) -> Dict[st
|
|
|
250
363
|
raise RuntimeError("Can only get access to AWS if authenticated.")
|
|
251
364
|
|
|
252
365
|
|
|
253
|
-
def _access_aws(*, storage_root: str, client: Client) ->
|
|
366
|
+
def _access_aws(*, storage_root: str, client: Client) -> dict[str, str]:
|
|
367
|
+
import lamindb_setup
|
|
368
|
+
|
|
254
369
|
response = client.functions.invoke(
|
|
255
370
|
"access-aws",
|
|
256
371
|
invoke_options={"body": {"storage_root": storage_root}},
|
|
@@ -276,7 +391,7 @@ def get_lamin_site_base_url():
|
|
|
276
391
|
return "https://lamin.ai"
|
|
277
392
|
|
|
278
393
|
|
|
279
|
-
def sign_up_local_hub(email) ->
|
|
394
|
+
def sign_up_local_hub(email) -> str | tuple[str, str, str]:
|
|
280
395
|
# raises gotrue.errors.AuthApiError: User already registered
|
|
281
396
|
password = base62(40) # generate new password
|
|
282
397
|
sign_up_kwargs = {"email": email, "password": password}
|
|
@@ -290,7 +405,7 @@ def sign_up_local_hub(email) -> Union[str, Tuple[str, str, str]]:
|
|
|
290
405
|
)
|
|
291
406
|
|
|
292
407
|
|
|
293
|
-
def _sign_in_hub(email: str, password: str, handle:
|
|
408
|
+
def _sign_in_hub(email: str, password: str, handle: str | None, client: Client):
|
|
294
409
|
auth = client.auth.sign_in_with_password(
|
|
295
410
|
{
|
|
296
411
|
"email": email,
|
|
@@ -320,8 +435,8 @@ def _sign_in_hub(email: str, password: str, handle: Optional[str], client: Clien
|
|
|
320
435
|
|
|
321
436
|
|
|
322
437
|
def sign_in_hub(
|
|
323
|
-
email: str, password: str, handle:
|
|
324
|
-
) ->
|
|
438
|
+
email: str, password: str, handle: str | None = None
|
|
439
|
+
) -> Exception | tuple[UUID, str, str, str, str]:
|
|
325
440
|
try:
|
|
326
441
|
result = call_with_fallback(
|
|
327
442
|
_sign_in_hub, email=email, password=password, handle=handle
|
lamindb_setup/core/_hub_crud.py
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
4
|
from uuid import UUID, uuid4
|
|
5
5
|
|
|
6
|
+
from lamin_utils import logger
|
|
7
|
+
from supabase.client import Client # noqa
|
|
8
|
+
|
|
6
9
|
|
|
7
10
|
def select_instance_by_owner_name(
|
|
8
11
|
owner: str,
|
|
9
12
|
name: str,
|
|
10
13
|
client: Client,
|
|
11
|
-
) ->
|
|
14
|
+
) -> dict | None:
|
|
12
15
|
try:
|
|
13
16
|
data = (
|
|
14
17
|
client.table("instance")
|
|
15
18
|
.select(
|
|
16
19
|
"*, account!inner!instance_account_id_28936e8f_fk_account_id(*),"
|
|
17
|
-
" storage(*)"
|
|
20
|
+
" storage!instance_storage_id_87963cc8_fk_storage_id(*)"
|
|
18
21
|
)
|
|
19
22
|
.eq("account.handle", owner)
|
|
20
23
|
.eq("name", name)
|
|
@@ -82,7 +85,10 @@ def select_instance_by_id_with_storage(
|
|
|
82
85
|
client: Client,
|
|
83
86
|
):
|
|
84
87
|
response = (
|
|
85
|
-
client.table("instance")
|
|
88
|
+
client.table("instance")
|
|
89
|
+
.select("*, storage!instance_storage_id_87963cc8_fk_storage_id(*)")
|
|
90
|
+
.eq("id", instance_id)
|
|
91
|
+
.execute()
|
|
86
92
|
)
|
|
87
93
|
if len(response.data) == 0:
|
|
88
94
|
return None
|
|
@@ -94,7 +100,7 @@ def update_instance(instance_id: str, instance_fields: dict, client: Client):
|
|
|
94
100
|
client.table("instance").update(instance_fields).eq("id", instance_id).execute()
|
|
95
101
|
)
|
|
96
102
|
if len(response.data) == 0:
|
|
97
|
-
raise
|
|
103
|
+
raise PermissionError(
|
|
98
104
|
f"Update of instance with {instance_id} was not successful. Probably, you"
|
|
99
105
|
" don't have sufficient permissions."
|
|
100
106
|
)
|
|
@@ -178,8 +184,11 @@ def select_db_user_by_instance(instance_id: str, client: Client):
|
|
|
178
184
|
def _delete_instance_record(instance_id: UUID, client: Client) -> None:
|
|
179
185
|
if not isinstance(instance_id, UUID):
|
|
180
186
|
instance_id = UUID(instance_id)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
187
|
+
response = client.table("instance").delete().eq("id", instance_id.hex).execute()
|
|
188
|
+
if response.data:
|
|
189
|
+
logger.important(f"deleted instance record on hub {instance_id.hex}")
|
|
190
|
+
else:
|
|
191
|
+
raise PermissionError(
|
|
192
|
+
f"Deleting of instance with {instance_id.hex} was not successful. Probably, you"
|
|
193
|
+
" don't have sufficient permissions."
|
|
194
|
+
)
|
lamindb_setup/core/_hub_utils.py
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from typing import Optional
|
|
4
|
+
|
|
2
5
|
from pydantic import BaseModel, validator
|
|
3
6
|
from pydantic.networks import MultiHostDsn
|
|
4
7
|
|
|
5
8
|
|
|
6
|
-
def validate_schema_arg(schema:
|
|
9
|
+
def validate_schema_arg(schema: str | None = None) -> str:
|
|
7
10
|
if schema is None or schema == "":
|
|
8
11
|
return ""
|
|
9
12
|
# currently no actual validation, can add back if we see a need
|
|
@@ -12,7 +15,7 @@ def validate_schema_arg(schema: Optional[str] = None) -> str:
|
|
|
12
15
|
return ",".join(to_be_validated)
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
def validate_db_arg(db:
|
|
18
|
+
def validate_db_arg(db: str | None) -> None:
|
|
16
19
|
if db is not None:
|
|
17
20
|
LaminDsnModel(db=db)
|
|
18
21
|
|
|
@@ -43,13 +46,13 @@ class LaminDsn(MultiHostDsn):
|
|
|
43
46
|
cls,
|
|
44
47
|
*,
|
|
45
48
|
scheme: str,
|
|
46
|
-
user:
|
|
47
|
-
password:
|
|
49
|
+
user: str | None = None,
|
|
50
|
+
password: str | None = None,
|
|
48
51
|
host: str,
|
|
49
|
-
port:
|
|
50
|
-
database:
|
|
51
|
-
query:
|
|
52
|
-
fragment:
|
|
52
|
+
port: str | None = None,
|
|
53
|
+
database: str | None = None,
|
|
54
|
+
query: str | None = None,
|
|
55
|
+
fragment: str | None = None,
|
|
53
56
|
**_kwargs: str,
|
|
54
57
|
) -> str:
|
|
55
58
|
return super().build(
|
lamindb_setup/core/_settings.py
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import os
|
|
2
|
-
from typing import
|
|
3
|
-
|
|
4
|
-
from
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from ._settings_load import (
|
|
5
7
|
load_instance_settings,
|
|
6
8
|
load_or_create_user_settings,
|
|
7
9
|
)
|
|
8
|
-
from ._settings_store import current_instance_settings_file
|
|
9
|
-
from pathlib import Path
|
|
10
|
-
from ._settings_store import settings_dir
|
|
10
|
+
from ._settings_store import current_instance_settings_file, settings_dir
|
|
11
11
|
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from pathlib import Path
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
"""Setup settings.
|
|
15
|
+
from lamindb_setup.core import InstanceSettings, StorageSettings, UserSettings
|
|
15
16
|
|
|
16
|
-
- :class:`~lamindb_setup.core.InstanceSettings`
|
|
17
|
-
- :class:`~lamindb_setup.core.StorageSettings`
|
|
18
|
-
- :class:`~lamindb_setup.core.UserSettings`
|
|
19
|
-
"""
|
|
20
17
|
|
|
21
|
-
|
|
18
|
+
class SetupSettings:
|
|
19
|
+
"""Setup settings."""
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
_instance_settings: Union[InstanceSettings, None] = None
|
|
21
|
+
_using_key: str | None = None # set through lamindb.settings
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
_user_settings: UserSettings | None = None
|
|
24
|
+
_instance_settings: InstanceSettings | None = None
|
|
25
|
+
|
|
26
|
+
_user_settings_env: str | None = None
|
|
27
|
+
_instance_settings_env: str | None = None
|
|
28
28
|
|
|
29
29
|
_auto_connect_path: Path = settings_dir / "auto_connect"
|
|
30
30
|
|
|
@@ -50,11 +50,8 @@ class SetupSettings:
|
|
|
50
50
|
|
|
51
51
|
@property
|
|
52
52
|
def user(self) -> UserSettings:
|
|
53
|
-
"""
|
|
54
|
-
if (
|
|
55
|
-
self._user_settings is None
|
|
56
|
-
or self._user_settings_env != get_env_name() # noqa
|
|
57
|
-
):
|
|
53
|
+
""":class:`~lamindb.setup.core.UserSettings`."""
|
|
54
|
+
if self._user_settings is None or self._user_settings_env != get_env_name():
|
|
58
55
|
self._user_settings = load_or_create_user_settings()
|
|
59
56
|
self._user_settings_env = get_env_name()
|
|
60
57
|
if self._user_settings and self._user_settings.uid is None:
|
|
@@ -63,10 +60,10 @@ class SetupSettings:
|
|
|
63
60
|
|
|
64
61
|
@property
|
|
65
62
|
def instance(self) -> InstanceSettings:
|
|
66
|
-
"""
|
|
63
|
+
""":class:`~lamindb.setup.core.InstanceSettings`."""
|
|
67
64
|
if (
|
|
68
65
|
self._instance_settings is None
|
|
69
|
-
or self._instance_settings_env != get_env_name()
|
|
66
|
+
or self._instance_settings_env != get_env_name()
|
|
70
67
|
):
|
|
71
68
|
self._instance_settings = load_instance_settings()
|
|
72
69
|
self._instance_settings_env = get_env_name()
|
|
@@ -74,13 +71,13 @@ class SetupSettings:
|
|
|
74
71
|
|
|
75
72
|
@property
|
|
76
73
|
def storage(self) -> StorageSettings:
|
|
77
|
-
"""
|
|
74
|
+
""":class:`~lamindb.setup.core.StorageSettings`."""
|
|
78
75
|
return self.instance.storage
|
|
79
76
|
|
|
80
77
|
@property
|
|
81
78
|
def _instance_exists(self):
|
|
82
79
|
try:
|
|
83
|
-
self.instance
|
|
80
|
+
self.instance # noqa
|
|
84
81
|
return True
|
|
85
82
|
# this is implicit logic that catches if no instance is loaded
|
|
86
83
|
except SystemExit:
|