lamindb_setup 0.77.4__py2.py3-none-any.whl → 0.77.5__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 +1 -1
- lamindb_setup/_cache.py +34 -34
- lamindb_setup/_check.py +7 -7
- lamindb_setup/_check_setup.py +79 -79
- lamindb_setup/_close.py +35 -35
- lamindb_setup/_connect_instance.py +431 -444
- lamindb_setup/_django.py +41 -41
- lamindb_setup/_entry_points.py +22 -22
- lamindb_setup/_exportdb.py +68 -68
- lamindb_setup/_importdb.py +50 -50
- lamindb_setup/_init_instance.py +417 -374
- lamindb_setup/_migrate.py +239 -239
- lamindb_setup/_register_instance.py +36 -36
- lamindb_setup/_schema.py +27 -27
- lamindb_setup/_schema_metadata.py +411 -411
- lamindb_setup/_set_managed_storage.py +55 -55
- lamindb_setup/_setup_user.py +137 -137
- lamindb_setup/_silence_loggers.py +44 -44
- lamindb_setup/core/__init__.py +21 -21
- lamindb_setup/core/_aws_credentials.py +151 -151
- lamindb_setup/core/_aws_storage.py +48 -48
- lamindb_setup/core/_deprecated.py +55 -55
- lamindb_setup/core/_docs.py +14 -14
- lamindb_setup/core/_hub_core.py +611 -590
- lamindb_setup/core/_hub_crud.py +211 -211
- lamindb_setup/core/_hub_utils.py +109 -109
- lamindb_setup/core/_private_django_api.py +88 -88
- lamindb_setup/core/_settings.py +138 -138
- lamindb_setup/core/_settings_instance.py +480 -467
- lamindb_setup/core/_settings_load.py +105 -105
- lamindb_setup/core/_settings_save.py +81 -81
- lamindb_setup/core/_settings_storage.py +412 -405
- lamindb_setup/core/_settings_store.py +75 -75
- lamindb_setup/core/_settings_user.py +53 -53
- lamindb_setup/core/_setup_bionty_sources.py +101 -101
- lamindb_setup/core/cloud_sqlite_locker.py +237 -232
- lamindb_setup/core/django.py +114 -114
- lamindb_setup/core/exceptions.py +12 -12
- lamindb_setup/core/hashing.py +114 -114
- lamindb_setup/core/types.py +19 -19
- lamindb_setup/core/upath.py +779 -779
- {lamindb_setup-0.77.4.dist-info → lamindb_setup-0.77.5.dist-info}/METADATA +1 -1
- lamindb_setup-0.77.5.dist-info/RECORD +47 -0
- {lamindb_setup-0.77.4.dist-info → lamindb_setup-0.77.5.dist-info}/WHEEL +1 -1
- lamindb_setup-0.77.4.dist-info/RECORD +0 -47
- {lamindb_setup-0.77.4.dist-info → lamindb_setup-0.77.5.dist-info}/LICENSE +0 -0
lamindb_setup/core/_hub_crud.py
CHANGED
|
@@ -1,211 +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
|
-
)
|
|
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
|
+
)
|
lamindb_setup/core/_hub_utils.py
CHANGED
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
from typing import Any, ClassVar, Optional
|
|
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}
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, ClassVar, Optional
|
|
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}
|