lamindb_setup 1.9.1__py3-none-any.whl → 1.10.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.
- lamindb_setup/__init__.py +107 -107
- lamindb_setup/_cache.py +87 -87
- lamindb_setup/_check_setup.py +192 -166
- lamindb_setup/_connect_instance.py +430 -328
- lamindb_setup/_delete.py +144 -141
- lamindb_setup/_disconnect.py +35 -32
- lamindb_setup/_init_instance.py +430 -440
- lamindb_setup/_migrate.py +278 -266
- lamindb_setup/_register_instance.py +32 -35
- lamindb_setup/_schema_metadata.py +441 -441
- lamindb_setup/_set_managed_storage.py +69 -70
- lamindb_setup/_setup_user.py +172 -133
- lamindb_setup/core/__init__.py +21 -21
- lamindb_setup/core/_aws_options.py +223 -223
- lamindb_setup/core/_aws_storage.py +9 -1
- lamindb_setup/core/_deprecated.py +1 -1
- lamindb_setup/core/_hub_client.py +248 -248
- lamindb_setup/core/_hub_core.py +751 -665
- lamindb_setup/core/_hub_crud.py +247 -227
- lamindb_setup/core/_private_django_api.py +83 -83
- lamindb_setup/core/_settings.py +374 -377
- lamindb_setup/core/_settings_instance.py +609 -569
- lamindb_setup/core/_settings_load.py +141 -141
- lamindb_setup/core/_settings_save.py +95 -95
- lamindb_setup/core/_settings_storage.py +427 -429
- lamindb_setup/core/_settings_store.py +91 -91
- lamindb_setup/core/_settings_user.py +55 -55
- lamindb_setup/core/_setup_bionty_sources.py +44 -44
- lamindb_setup/core/cloud_sqlite_locker.py +240 -240
- lamindb_setup/core/django.py +311 -305
- lamindb_setup/core/exceptions.py +1 -1
- lamindb_setup/core/hashing.py +134 -134
- lamindb_setup/core/types.py +1 -1
- lamindb_setup/core/upath.py +1013 -1013
- lamindb_setup/errors.py +80 -70
- lamindb_setup/types.py +20 -20
- {lamindb_setup-1.9.1.dist-info → lamindb_setup-1.10.1.dist-info}/METADATA +4 -4
- lamindb_setup-1.10.1.dist-info/RECORD +50 -0
- lamindb_setup-1.9.1.dist-info/RECORD +0 -50
- {lamindb_setup-1.9.1.dist-info → lamindb_setup-1.10.1.dist-info}/LICENSE +0 -0
- {lamindb_setup-1.9.1.dist-info → lamindb_setup-1.10.1.dist-info}/WHEEL +0 -0
|
@@ -1,248 +1,248 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import json
|
|
4
|
-
import os
|
|
5
|
-
from typing import Literal
|
|
6
|
-
from urllib.request import urlretrieve
|
|
7
|
-
|
|
8
|
-
from httpx import HTTPTransport
|
|
9
|
-
from lamin_utils import logger
|
|
10
|
-
from pydantic_settings import BaseSettings
|
|
11
|
-
from supabase import Client, create_client # type: ignore
|
|
12
|
-
from supabase.lib.client_options import ClientOptions
|
|
13
|
-
|
|
14
|
-
from ._settings_save import save_user_settings
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class Connector(BaseSettings):
|
|
18
|
-
url: str
|
|
19
|
-
key: str
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def load_fallback_connector() -> Connector:
|
|
23
|
-
url = "https://lamin-site-assets.s3.amazonaws.com/connector.env"
|
|
24
|
-
connector_file, _ = urlretrieve(url)
|
|
25
|
-
connector = Connector(_env_file=connector_file)
|
|
26
|
-
return connector
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
PROD_URL = "https://hub.lamin.ai"
|
|
30
|
-
PROD_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxhZXNhdW1tZHlkbGxwcGdmY2h1Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTY4NDA1NTEsImV4cCI6MTk3MjQxNjU1MX0.WUeCRiun0ExUxKIv5-CtjF6878H8u26t0JmCWx3_2-c"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class Environment:
|
|
34
|
-
def __init__(self, fallback: bool = False):
|
|
35
|
-
lamin_env = os.getenv("LAMIN_ENV")
|
|
36
|
-
if lamin_env is None:
|
|
37
|
-
lamin_env = "prod"
|
|
38
|
-
# set public key
|
|
39
|
-
if lamin_env == "prod":
|
|
40
|
-
if not fallback:
|
|
41
|
-
url = PROD_URL
|
|
42
|
-
key = PROD_ANON_KEY
|
|
43
|
-
else:
|
|
44
|
-
connector = load_fallback_connector()
|
|
45
|
-
url = connector.url
|
|
46
|
-
key = connector.key
|
|
47
|
-
elif lamin_env == "staging":
|
|
48
|
-
url = "https://amvrvdwndlqdzgedrqdv.supabase.co"
|
|
49
|
-
key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFtdnJ2ZHduZGxxZHpnZWRycWR2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzcxNTcxMzMsImV4cCI6MTk5MjczMzEzM30.Gelt3dQEi8tT4j-JA36RbaZuUvxRnczvRr3iyRtzjY0"
|
|
50
|
-
elif lamin_env == "staging-test":
|
|
51
|
-
url = "https://iugyyajllqftbpidapak.supabase.co"
|
|
52
|
-
key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Iml1Z3l5YWpsbHFmdGJwaWRhcGFrIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTQyMjYyODMsImV4cCI6MjAwOTgwMjI4M30.s7B0gMogFhUatMSwlfuPJ95kWhdCZMn1ROhZ3t6Og90"
|
|
53
|
-
elif lamin_env == "prod-test":
|
|
54
|
-
url = "https://xtdacpwiqwpbxsatoyrv.supabase.co"
|
|
55
|
-
key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh0ZGFjcHdpcXdwYnhzYXRveXJ2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTQyMjYxNDIsImV4cCI6MjAwOTgwMjE0Mn0.Dbi27qujTt8Ei9gfp9KnEWTYptE5KUbZzEK6boL46k4"
|
|
56
|
-
else:
|
|
57
|
-
url = os.environ["SUPABASE_API_URL"]
|
|
58
|
-
key = os.environ["SUPABASE_ANON_KEY"]
|
|
59
|
-
self.lamin_env: str = lamin_env
|
|
60
|
-
self.supabase_api_url: str = url
|
|
61
|
-
self.supabase_anon_key: str = key
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
DEFAULT_TIMEOUT = 20
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
# runs ~0.5s
|
|
68
|
-
def connect_hub(
|
|
69
|
-
fallback_env: bool = False, client_options: ClientOptions | None = None
|
|
70
|
-
) -> Client:
|
|
71
|
-
env = Environment(fallback=fallback_env)
|
|
72
|
-
if client_options is None:
|
|
73
|
-
client_options = ClientOptions(
|
|
74
|
-
auto_refresh_token=False,
|
|
75
|
-
function_client_timeout=DEFAULT_TIMEOUT,
|
|
76
|
-
postgrest_client_timeout=DEFAULT_TIMEOUT,
|
|
77
|
-
)
|
|
78
|
-
client = create_client(env.supabase_api_url, env.supabase_anon_key, client_options)
|
|
79
|
-
# needed to enable retries for http requests in supabase
|
|
80
|
-
# these are separate clients and need separate transports
|
|
81
|
-
# retries are done only in case an httpx.ConnectError or an httpx.ConnectTimeout occurs
|
|
82
|
-
transport_kwargs = {"verify": True, "http2": True, "retries": 2}
|
|
83
|
-
client.auth._http_client._transport = HTTPTransport(**transport_kwargs)
|
|
84
|
-
client.functions._client._transport = HTTPTransport(**transport_kwargs)
|
|
85
|
-
client.postgrest.session._transport = HTTPTransport(**transport_kwargs)
|
|
86
|
-
return client
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def connect_hub_with_auth(
|
|
90
|
-
fallback_env: bool = False,
|
|
91
|
-
renew_token: bool = False,
|
|
92
|
-
access_token: str | None = None,
|
|
93
|
-
) -> Client:
|
|
94
|
-
hub = connect_hub(fallback_env=fallback_env)
|
|
95
|
-
if access_token is None:
|
|
96
|
-
from lamindb_setup import settings
|
|
97
|
-
|
|
98
|
-
if renew_token:
|
|
99
|
-
settings.user.access_token = get_access_token(
|
|
100
|
-
settings.user.email, settings.user.password, settings.user.api_key
|
|
101
|
-
)
|
|
102
|
-
access_token = settings.user.access_token
|
|
103
|
-
hub.postgrest.auth(access_token)
|
|
104
|
-
hub.functions.set_auth(access_token)
|
|
105
|
-
return hub
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
# runs ~0.5s
|
|
109
|
-
def get_access_token(
|
|
110
|
-
email: str | None = None, password: str | None = None, api_key: str | None = None
|
|
111
|
-
):
|
|
112
|
-
hub = connect_hub()
|
|
113
|
-
try:
|
|
114
|
-
if api_key is not None:
|
|
115
|
-
auth_response = hub.functions.invoke(
|
|
116
|
-
"get-jwt-v1",
|
|
117
|
-
invoke_options={"body": {"api_key": api_key}},
|
|
118
|
-
)
|
|
119
|
-
return json.loads(auth_response)["accessToken"]
|
|
120
|
-
auth_response = hub.auth.sign_in_with_password(
|
|
121
|
-
{
|
|
122
|
-
"email": email,
|
|
123
|
-
"password": password,
|
|
124
|
-
}
|
|
125
|
-
)
|
|
126
|
-
return auth_response.session.access_token
|
|
127
|
-
except Exception as e:
|
|
128
|
-
# we need to log the problem here because the exception is usually caught outside
|
|
129
|
-
# in call_with_fallback_auth
|
|
130
|
-
logger.warning(f"failed to update your lamindb access token: {e}")
|
|
131
|
-
raise e
|
|
132
|
-
finally:
|
|
133
|
-
hub.auth.sign_out(options={"scope": "local"})
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def call_with_fallback_auth(
|
|
137
|
-
callable,
|
|
138
|
-
**kwargs,
|
|
139
|
-
):
|
|
140
|
-
access_token = kwargs.pop("access_token", None)
|
|
141
|
-
|
|
142
|
-
if access_token is not None:
|
|
143
|
-
try:
|
|
144
|
-
client = connect_hub_with_auth(access_token=access_token)
|
|
145
|
-
result = callable(**kwargs, client=client)
|
|
146
|
-
finally:
|
|
147
|
-
try:
|
|
148
|
-
client.auth.sign_out(options={"scope": "local"})
|
|
149
|
-
except NameError:
|
|
150
|
-
pass
|
|
151
|
-
return result
|
|
152
|
-
|
|
153
|
-
for renew_token, fallback_env in [(False, False), (True, False), (False, True)]:
|
|
154
|
-
try:
|
|
155
|
-
client = connect_hub_with_auth(
|
|
156
|
-
renew_token=renew_token, fallback_env=fallback_env
|
|
157
|
-
)
|
|
158
|
-
result = callable(**kwargs, client=client)
|
|
159
|
-
# we update access_token here
|
|
160
|
-
# because at this point the call has been successfully resolved
|
|
161
|
-
if renew_token:
|
|
162
|
-
from lamindb_setup import settings
|
|
163
|
-
|
|
164
|
-
# here settings.user contains an updated access_token
|
|
165
|
-
save_user_settings(settings.user)
|
|
166
|
-
break
|
|
167
|
-
# we use Exception here as the ways in which the client fails upon 401
|
|
168
|
-
# are not consistent and keep changing
|
|
169
|
-
# because we ultimately raise the error, it's OK I'd say
|
|
170
|
-
except Exception as e:
|
|
171
|
-
if fallback_env:
|
|
172
|
-
raise e
|
|
173
|
-
finally:
|
|
174
|
-
try:
|
|
175
|
-
client.auth.sign_out(options={"scope": "local"})
|
|
176
|
-
except NameError:
|
|
177
|
-
pass
|
|
178
|
-
return result
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def call_with_fallback(
|
|
182
|
-
callable,
|
|
183
|
-
**kwargs,
|
|
184
|
-
):
|
|
185
|
-
for fallback_env in [False, True]:
|
|
186
|
-
try:
|
|
187
|
-
client = connect_hub(fallback_env=fallback_env)
|
|
188
|
-
result = callable(**kwargs, client=client)
|
|
189
|
-
break
|
|
190
|
-
except Exception as e:
|
|
191
|
-
if fallback_env:
|
|
192
|
-
raise e
|
|
193
|
-
finally:
|
|
194
|
-
try:
|
|
195
|
-
# in case there was sign in
|
|
196
|
-
client.auth.sign_out(options={"scope": "local"})
|
|
197
|
-
except NameError:
|
|
198
|
-
pass
|
|
199
|
-
return result
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
def requests_client():
|
|
203
|
-
# local is used in tests
|
|
204
|
-
if os.environ.get("LAMIN_ENV", "prod") == "local":
|
|
205
|
-
from fastapi.testclient import TestClient
|
|
206
|
-
from laminhub_rest.main import app
|
|
207
|
-
|
|
208
|
-
return TestClient(app)
|
|
209
|
-
|
|
210
|
-
import requests # type: ignore
|
|
211
|
-
|
|
212
|
-
return requests
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
def request_with_auth(
|
|
216
|
-
url: str,
|
|
217
|
-
method: Literal["get", "post", "put", "delete", "head", "options"],
|
|
218
|
-
access_token: str,
|
|
219
|
-
renew_token: bool = True,
|
|
220
|
-
**kwargs,
|
|
221
|
-
):
|
|
222
|
-
requests = requests_client()
|
|
223
|
-
|
|
224
|
-
headers = kwargs.pop("headers", {})
|
|
225
|
-
headers["Authorization"] = f"Bearer {access_token}"
|
|
226
|
-
|
|
227
|
-
make_request = getattr(requests, method)
|
|
228
|
-
timeout = kwargs.pop("timeout", DEFAULT_TIMEOUT)
|
|
229
|
-
|
|
230
|
-
response = make_request(url, headers=headers, timeout=timeout, **kwargs)
|
|
231
|
-
status_code = response.status_code
|
|
232
|
-
# update access_token and try again if failed
|
|
233
|
-
if not (200 <= status_code < 300) and renew_token:
|
|
234
|
-
from lamindb_setup import settings
|
|
235
|
-
|
|
236
|
-
logger.debug(f"{method} {url} failed: {status_code} {response.text}")
|
|
237
|
-
|
|
238
|
-
access_token = get_access_token(
|
|
239
|
-
settings.user.email, settings.user.password, settings.user.api_key
|
|
240
|
-
)
|
|
241
|
-
|
|
242
|
-
settings.user.access_token = access_token
|
|
243
|
-
save_user_settings(settings.user)
|
|
244
|
-
|
|
245
|
-
headers["Authorization"] = f"Bearer {access_token}"
|
|
246
|
-
|
|
247
|
-
response = make_request(url, headers=headers, timeout=timeout, **kwargs)
|
|
248
|
-
return response
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from typing import Literal
|
|
6
|
+
from urllib.request import urlretrieve
|
|
7
|
+
|
|
8
|
+
from httpx import HTTPTransport
|
|
9
|
+
from lamin_utils import logger
|
|
10
|
+
from pydantic_settings import BaseSettings
|
|
11
|
+
from supabase import Client, create_client # type: ignore
|
|
12
|
+
from supabase.lib.client_options import ClientOptions
|
|
13
|
+
|
|
14
|
+
from ._settings_save import save_user_settings
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Connector(BaseSettings):
|
|
18
|
+
url: str
|
|
19
|
+
key: str
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def load_fallback_connector() -> Connector:
|
|
23
|
+
url = "https://lamin-site-assets.s3.amazonaws.com/connector.env"
|
|
24
|
+
connector_file, _ = urlretrieve(url)
|
|
25
|
+
connector = Connector(_env_file=connector_file)
|
|
26
|
+
return connector
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
PROD_URL = "https://hub.lamin.ai"
|
|
30
|
+
PROD_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxhZXNhdW1tZHlkbGxwcGdmY2h1Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NTY4NDA1NTEsImV4cCI6MTk3MjQxNjU1MX0.WUeCRiun0ExUxKIv5-CtjF6878H8u26t0JmCWx3_2-c"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Environment:
|
|
34
|
+
def __init__(self, fallback: bool = False):
|
|
35
|
+
lamin_env = os.getenv("LAMIN_ENV")
|
|
36
|
+
if lamin_env is None:
|
|
37
|
+
lamin_env = "prod"
|
|
38
|
+
# set public key
|
|
39
|
+
if lamin_env == "prod":
|
|
40
|
+
if not fallback:
|
|
41
|
+
url = PROD_URL
|
|
42
|
+
key = PROD_ANON_KEY
|
|
43
|
+
else:
|
|
44
|
+
connector = load_fallback_connector()
|
|
45
|
+
url = connector.url
|
|
46
|
+
key = connector.key
|
|
47
|
+
elif lamin_env == "staging":
|
|
48
|
+
url = "https://amvrvdwndlqdzgedrqdv.supabase.co"
|
|
49
|
+
key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImFtdnJ2ZHduZGxxZHpnZWRycWR2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2NzcxNTcxMzMsImV4cCI6MTk5MjczMzEzM30.Gelt3dQEi8tT4j-JA36RbaZuUvxRnczvRr3iyRtzjY0"
|
|
50
|
+
elif lamin_env == "staging-test":
|
|
51
|
+
url = "https://iugyyajllqftbpidapak.supabase.co"
|
|
52
|
+
key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Iml1Z3l5YWpsbHFmdGJwaWRhcGFrIiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTQyMjYyODMsImV4cCI6MjAwOTgwMjI4M30.s7B0gMogFhUatMSwlfuPJ95kWhdCZMn1ROhZ3t6Og90"
|
|
53
|
+
elif lamin_env == "prod-test":
|
|
54
|
+
url = "https://xtdacpwiqwpbxsatoyrv.supabase.co"
|
|
55
|
+
key = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh0ZGFjcHdpcXdwYnhzYXRveXJ2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTQyMjYxNDIsImV4cCI6MjAwOTgwMjE0Mn0.Dbi27qujTt8Ei9gfp9KnEWTYptE5KUbZzEK6boL46k4"
|
|
56
|
+
else:
|
|
57
|
+
url = os.environ["SUPABASE_API_URL"]
|
|
58
|
+
key = os.environ["SUPABASE_ANON_KEY"]
|
|
59
|
+
self.lamin_env: str = lamin_env
|
|
60
|
+
self.supabase_api_url: str = url
|
|
61
|
+
self.supabase_anon_key: str = key
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
DEFAULT_TIMEOUT = 20
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# runs ~0.5s
|
|
68
|
+
def connect_hub(
|
|
69
|
+
fallback_env: bool = False, client_options: ClientOptions | None = None
|
|
70
|
+
) -> Client:
|
|
71
|
+
env = Environment(fallback=fallback_env)
|
|
72
|
+
if client_options is None:
|
|
73
|
+
client_options = ClientOptions(
|
|
74
|
+
auto_refresh_token=False,
|
|
75
|
+
function_client_timeout=DEFAULT_TIMEOUT,
|
|
76
|
+
postgrest_client_timeout=DEFAULT_TIMEOUT,
|
|
77
|
+
)
|
|
78
|
+
client = create_client(env.supabase_api_url, env.supabase_anon_key, client_options)
|
|
79
|
+
# needed to enable retries for http requests in supabase
|
|
80
|
+
# these are separate clients and need separate transports
|
|
81
|
+
# retries are done only in case an httpx.ConnectError or an httpx.ConnectTimeout occurs
|
|
82
|
+
transport_kwargs = {"verify": True, "http2": True, "retries": 2}
|
|
83
|
+
client.auth._http_client._transport = HTTPTransport(**transport_kwargs)
|
|
84
|
+
client.functions._client._transport = HTTPTransport(**transport_kwargs)
|
|
85
|
+
client.postgrest.session._transport = HTTPTransport(**transport_kwargs)
|
|
86
|
+
return client
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def connect_hub_with_auth(
|
|
90
|
+
fallback_env: bool = False,
|
|
91
|
+
renew_token: bool = False,
|
|
92
|
+
access_token: str | None = None,
|
|
93
|
+
) -> Client:
|
|
94
|
+
hub = connect_hub(fallback_env=fallback_env)
|
|
95
|
+
if access_token is None:
|
|
96
|
+
from lamindb_setup import settings
|
|
97
|
+
|
|
98
|
+
if renew_token:
|
|
99
|
+
settings.user.access_token = get_access_token(
|
|
100
|
+
settings.user.email, settings.user.password, settings.user.api_key
|
|
101
|
+
)
|
|
102
|
+
access_token = settings.user.access_token
|
|
103
|
+
hub.postgrest.auth(access_token)
|
|
104
|
+
hub.functions.set_auth(access_token)
|
|
105
|
+
return hub
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# runs ~0.5s
|
|
109
|
+
def get_access_token(
|
|
110
|
+
email: str | None = None, password: str | None = None, api_key: str | None = None
|
|
111
|
+
):
|
|
112
|
+
hub = connect_hub()
|
|
113
|
+
try:
|
|
114
|
+
if api_key is not None:
|
|
115
|
+
auth_response = hub.functions.invoke(
|
|
116
|
+
"get-jwt-v1",
|
|
117
|
+
invoke_options={"body": {"api_key": api_key}},
|
|
118
|
+
)
|
|
119
|
+
return json.loads(auth_response)["accessToken"]
|
|
120
|
+
auth_response = hub.auth.sign_in_with_password(
|
|
121
|
+
{
|
|
122
|
+
"email": email,
|
|
123
|
+
"password": password,
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
return auth_response.session.access_token
|
|
127
|
+
except Exception as e:
|
|
128
|
+
# we need to log the problem here because the exception is usually caught outside
|
|
129
|
+
# in call_with_fallback_auth
|
|
130
|
+
logger.warning(f"failed to update your lamindb access token: {e}")
|
|
131
|
+
raise e
|
|
132
|
+
finally:
|
|
133
|
+
hub.auth.sign_out(options={"scope": "local"})
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def call_with_fallback_auth(
|
|
137
|
+
callable,
|
|
138
|
+
**kwargs,
|
|
139
|
+
):
|
|
140
|
+
access_token = kwargs.pop("access_token", None)
|
|
141
|
+
|
|
142
|
+
if access_token is not None:
|
|
143
|
+
try:
|
|
144
|
+
client = connect_hub_with_auth(access_token=access_token)
|
|
145
|
+
result = callable(**kwargs, client=client)
|
|
146
|
+
finally:
|
|
147
|
+
try:
|
|
148
|
+
client.auth.sign_out(options={"scope": "local"})
|
|
149
|
+
except NameError:
|
|
150
|
+
pass
|
|
151
|
+
return result
|
|
152
|
+
|
|
153
|
+
for renew_token, fallback_env in [(False, False), (True, False), (False, True)]:
|
|
154
|
+
try:
|
|
155
|
+
client = connect_hub_with_auth(
|
|
156
|
+
renew_token=renew_token, fallback_env=fallback_env
|
|
157
|
+
)
|
|
158
|
+
result = callable(**kwargs, client=client)
|
|
159
|
+
# we update access_token here
|
|
160
|
+
# because at this point the call has been successfully resolved
|
|
161
|
+
if renew_token:
|
|
162
|
+
from lamindb_setup import settings
|
|
163
|
+
|
|
164
|
+
# here settings.user contains an updated access_token
|
|
165
|
+
save_user_settings(settings.user)
|
|
166
|
+
break
|
|
167
|
+
# we use Exception here as the ways in which the client fails upon 401
|
|
168
|
+
# are not consistent and keep changing
|
|
169
|
+
# because we ultimately raise the error, it's OK I'd say
|
|
170
|
+
except Exception as e:
|
|
171
|
+
if fallback_env:
|
|
172
|
+
raise e
|
|
173
|
+
finally:
|
|
174
|
+
try:
|
|
175
|
+
client.auth.sign_out(options={"scope": "local"})
|
|
176
|
+
except NameError:
|
|
177
|
+
pass
|
|
178
|
+
return result
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def call_with_fallback(
|
|
182
|
+
callable,
|
|
183
|
+
**kwargs,
|
|
184
|
+
):
|
|
185
|
+
for fallback_env in [False, True]:
|
|
186
|
+
try:
|
|
187
|
+
client = connect_hub(fallback_env=fallback_env)
|
|
188
|
+
result = callable(**kwargs, client=client)
|
|
189
|
+
break
|
|
190
|
+
except Exception as e:
|
|
191
|
+
if fallback_env:
|
|
192
|
+
raise e
|
|
193
|
+
finally:
|
|
194
|
+
try:
|
|
195
|
+
# in case there was sign in
|
|
196
|
+
client.auth.sign_out(options={"scope": "local"})
|
|
197
|
+
except NameError:
|
|
198
|
+
pass
|
|
199
|
+
return result
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def requests_client():
|
|
203
|
+
# local is used in tests
|
|
204
|
+
if os.environ.get("LAMIN_ENV", "prod") == "local":
|
|
205
|
+
from fastapi.testclient import TestClient
|
|
206
|
+
from laminhub_rest.main import app
|
|
207
|
+
|
|
208
|
+
return TestClient(app)
|
|
209
|
+
|
|
210
|
+
import requests # type: ignore
|
|
211
|
+
|
|
212
|
+
return requests
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def request_with_auth(
|
|
216
|
+
url: str,
|
|
217
|
+
method: Literal["get", "post", "put", "delete", "head", "options"],
|
|
218
|
+
access_token: str,
|
|
219
|
+
renew_token: bool = True,
|
|
220
|
+
**kwargs,
|
|
221
|
+
):
|
|
222
|
+
requests = requests_client()
|
|
223
|
+
|
|
224
|
+
headers = kwargs.pop("headers", {})
|
|
225
|
+
headers["Authorization"] = f"Bearer {access_token}"
|
|
226
|
+
|
|
227
|
+
make_request = getattr(requests, method)
|
|
228
|
+
timeout = kwargs.pop("timeout", DEFAULT_TIMEOUT)
|
|
229
|
+
|
|
230
|
+
response = make_request(url, headers=headers, timeout=timeout, **kwargs)
|
|
231
|
+
status_code = response.status_code
|
|
232
|
+
# update access_token and try again if failed
|
|
233
|
+
if not (200 <= status_code < 300) and renew_token:
|
|
234
|
+
from lamindb_setup import settings
|
|
235
|
+
|
|
236
|
+
logger.debug(f"{method} {url} failed: {status_code} {response.text}")
|
|
237
|
+
|
|
238
|
+
access_token = get_access_token(
|
|
239
|
+
settings.user.email, settings.user.password, settings.user.api_key
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
settings.user.access_token = access_token
|
|
243
|
+
save_user_settings(settings.user)
|
|
244
|
+
|
|
245
|
+
headers["Authorization"] = f"Bearer {access_token}"
|
|
246
|
+
|
|
247
|
+
response = make_request(url, headers=headers, timeout=timeout, **kwargs)
|
|
248
|
+
return response
|