lamindb_setup 1.19.0__py3-none-any.whl → 1.19.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.
Files changed (49) hide show
  1. lamindb_setup/__init__.py +1 -1
  2. lamindb_setup/_cache.py +87 -87
  3. lamindb_setup/_check.py +7 -7
  4. lamindb_setup/_check_setup.py +131 -131
  5. lamindb_setup/_connect_instance.py +443 -441
  6. lamindb_setup/_delete.py +155 -155
  7. lamindb_setup/_disconnect.py +38 -38
  8. lamindb_setup/_django.py +39 -39
  9. lamindb_setup/_entry_points.py +19 -19
  10. lamindb_setup/_init_instance.py +423 -423
  11. lamindb_setup/_migrate.py +331 -331
  12. lamindb_setup/_register_instance.py +32 -32
  13. lamindb_setup/_schema.py +27 -27
  14. lamindb_setup/_schema_metadata.py +451 -451
  15. lamindb_setup/_set_managed_storage.py +81 -81
  16. lamindb_setup/_setup_user.py +198 -198
  17. lamindb_setup/_silence_loggers.py +46 -46
  18. lamindb_setup/core/__init__.py +25 -34
  19. lamindb_setup/core/_aws_options.py +276 -276
  20. lamindb_setup/core/_aws_storage.py +57 -57
  21. lamindb_setup/core/_clone.py +50 -50
  22. lamindb_setup/core/_deprecated.py +62 -62
  23. lamindb_setup/core/_docs.py +14 -14
  24. lamindb_setup/core/_hub_client.py +288 -288
  25. lamindb_setup/core/_hub_crud.py +247 -247
  26. lamindb_setup/core/_hub_utils.py +100 -100
  27. lamindb_setup/core/_private_django_api.py +80 -80
  28. lamindb_setup/core/_settings.py +440 -434
  29. lamindb_setup/core/_settings_instance.py +22 -1
  30. lamindb_setup/core/_settings_load.py +162 -162
  31. lamindb_setup/core/_settings_save.py +108 -108
  32. lamindb_setup/core/_settings_storage.py +433 -433
  33. lamindb_setup/core/_settings_store.py +162 -162
  34. lamindb_setup/core/_settings_user.py +55 -55
  35. lamindb_setup/core/_setup_bionty_sources.py +44 -44
  36. lamindb_setup/core/cloud_sqlite_locker.py +240 -240
  37. lamindb_setup/core/django.py +414 -413
  38. lamindb_setup/core/exceptions.py +1 -1
  39. lamindb_setup/core/hashing.py +134 -134
  40. lamindb_setup/core/types.py +1 -1
  41. lamindb_setup/core/upath.py +1031 -1028
  42. lamindb_setup/errors.py +72 -72
  43. lamindb_setup/io.py +423 -423
  44. lamindb_setup/types.py +17 -17
  45. {lamindb_setup-1.19.0.dist-info → lamindb_setup-1.19.1.dist-info}/METADATA +3 -2
  46. lamindb_setup-1.19.1.dist-info/RECORD +51 -0
  47. {lamindb_setup-1.19.0.dist-info → lamindb_setup-1.19.1.dist-info}/WHEEL +1 -1
  48. {lamindb_setup-1.19.0.dist-info → lamindb_setup-1.19.1.dist-info/licenses}/LICENSE +201 -201
  49. lamindb_setup-1.19.0.dist-info/RECORD +0 -51
@@ -1,288 +1,288 @@
1
- from __future__ import annotations
2
-
3
- import json
4
- import os
5
- from contextlib import contextmanager
6
- from datetime import datetime
7
- from typing import Literal
8
- from urllib.request import urlretrieve
9
-
10
- import httpx
11
- from httpx_retries import Retry, RetryTransport
12
- from lamin_utils import logger
13
- from supabase import Client, ClientOptions, create_client
14
-
15
- from ._settings_save import save_user_settings
16
- from ._settings_store import Connector
17
-
18
-
19
- def load_fallback_connector() -> Connector:
20
- url = "https://lamin-site-assets.s3.amazonaws.com/connector.env"
21
- connector_file, _ = urlretrieve(url)
22
- return Connector.from_env_file(connector_file, "")
23
-
24
-
25
- PROD_URL = "https://hub.lamin.ai"
26
- PROD_ANON_KEY = "sb_publishable_YVa4h8hQ-yBhXpfa2cP39w_PhoLW6Nu"
27
-
28
-
29
- class Environment:
30
- def __init__(self, fallback: bool = False):
31
- lamin_env = os.getenv("LAMIN_ENV")
32
- if lamin_env is None:
33
- lamin_env = "prod"
34
- # set public key
35
- if lamin_env == "prod":
36
- if not fallback:
37
- url = PROD_URL
38
- key = PROD_ANON_KEY
39
- else:
40
- connector = load_fallback_connector()
41
- url = connector.url
42
- key = connector.key
43
- elif lamin_env == "staging":
44
- url = "https://amvrvdwndlqdzgedrqdv.supabase.co"
45
- key = "sb_publishable_amVjtilv_Yj4VmGLmxtq6A_sYlLoQx5"
46
- elif lamin_env == "staging-test":
47
- url = "https://iugyyajllqftbpidapak.supabase.co"
48
- key = "sb_publishable_XmXroXqTLQw-eeT5kysCww_k8vJv-4L"
49
- elif lamin_env == "prod-test":
50
- url = "https://xtdacpwiqwpbxsatoyrv.supabase.co"
51
- key = "sb_publishable_G-pyO5aW6VFErTzJyVvM5w_NAv1_Mf7"
52
- else:
53
- url = os.environ["SUPABASE_API_URL"]
54
- key = os.environ["SUPABASE_ANON_KEY"]
55
- self.lamin_env: str = lamin_env
56
- self.supabase_api_url: str = url
57
- self.supabase_anon_key: str = key
58
-
59
-
60
- DEFAULT_TIMEOUT = 12
61
-
62
-
63
- # needed to log retries
64
- class LogRetry(Retry):
65
- def increment(self):
66
- new = super().increment()
67
- now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
68
- # new.attempts_made is the 1-based retry count
69
- logger.warning(f"{now} HTTP retry attempt {new.attempts_made}/{new.total}")
70
- return new
71
-
72
-
73
- # runs ~0.5s
74
- def connect_hub(
75
- fallback_env: bool = False, client_options: ClientOptions | None = None
76
- ) -> Client:
77
- env = Environment(fallback=fallback_env)
78
- if client_options is None:
79
- client_options = ClientOptions(
80
- auto_refresh_token=False,
81
- function_client_timeout=DEFAULT_TIMEOUT,
82
- postgrest_client_timeout=DEFAULT_TIMEOUT,
83
- )
84
- client = create_client(env.supabase_api_url, env.supabase_anon_key, client_options)
85
- # needed to enable retries for http requests in supabase
86
- # these are separate clients and need separate transports
87
- transports = []
88
- for _ in range(2):
89
- transports.append(
90
- RetryTransport(
91
- retry=LogRetry(total=2, backoff_factor=0.2),
92
- transport=httpx.HTTPTransport(verify=True, http2=True, trust_env=True),
93
- )
94
- )
95
- # this overwrites transports of existing httpx clients
96
- # if proxies are set, the default transports that were created on clients init
97
- # will be used, irrespective of these re-settings
98
- client.auth._http_client._transport = transports[0]
99
- client.postgrest.session._transport = transports[1]
100
- # POST is not retryable by default, but for our functions it should be safe to retry
101
- client.functions._client._transport = RetryTransport(
102
- retry=LogRetry(
103
- total=2,
104
- backoff_factor=0.2,
105
- allowed_methods=[
106
- "HEAD",
107
- "GET",
108
- "PUT",
109
- "DELETE",
110
- "OPTIONS",
111
- "TRACE",
112
- "POST",
113
- ],
114
- ),
115
- transport=httpx.HTTPTransport(verify=True, http2=True, trust_env=True),
116
- )
117
- return client
118
-
119
-
120
- def connect_hub_with_auth(
121
- fallback_env: bool = False,
122
- renew_token: bool = False,
123
- access_token: str | None = None,
124
- ) -> Client:
125
- hub = connect_hub(fallback_env=fallback_env)
126
- if access_token is None:
127
- from lamindb_setup import settings
128
-
129
- if renew_token:
130
- settings.user.access_token = get_access_token(
131
- settings.user.email, settings.user.password, settings.user.api_key
132
- )
133
- access_token = settings.user.access_token
134
- hub.postgrest.auth(access_token)
135
- hub.functions.set_auth(access_token)
136
- return hub
137
-
138
-
139
- # runs ~0.5s
140
- def get_access_token(
141
- email: str | None = None, password: str | None = None, api_key: str | None = None
142
- ):
143
- hub = connect_hub()
144
- try:
145
- if api_key is not None:
146
- auth_response = hub.functions.invoke(
147
- "get-jwt-v1",
148
- invoke_options={"body": {"api_key": api_key}},
149
- )
150
- return json.loads(auth_response)["accessToken"]
151
- auth_response = hub.auth.sign_in_with_password(
152
- {
153
- "email": email,
154
- "password": password,
155
- }
156
- )
157
- return auth_response.session.access_token
158
- except Exception as e:
159
- # we need to log the problem here because the exception is usually caught outside
160
- # in call_with_fallback_auth
161
- logger.warning(f"failed to update your lamindb access token: {e}")
162
- raise e
163
- finally:
164
- hub.auth.sign_out(options={"scope": "local"})
165
-
166
-
167
- def call_with_fallback_auth(
168
- callable,
169
- **kwargs,
170
- ):
171
- access_token = kwargs.pop("access_token", None)
172
-
173
- if access_token is not None:
174
- client = None
175
- try:
176
- client = connect_hub_with_auth(access_token=access_token)
177
- result = callable(**kwargs, client=client)
178
- finally:
179
- if client is not None:
180
- client.auth.sign_out(options={"scope": "local"})
181
-
182
- return result
183
-
184
- for renew_token, fallback_env in [(False, False), (True, False), (False, True)]:
185
- client = None
186
- try:
187
- client = connect_hub_with_auth(
188
- renew_token=renew_token, fallback_env=fallback_env
189
- )
190
- result = callable(**kwargs, client=client)
191
- # we update access_token here
192
- # because at this point the call has been successfully resolved
193
- if renew_token:
194
- from lamindb_setup import settings
195
-
196
- # here settings.user contains an updated access_token
197
- save_user_settings(settings.user)
198
- break
199
- # we use Exception here as the ways in which the client fails upon 401
200
- # are not consistent and keep changing
201
- # because we ultimately raise the error, it's OK I'd say
202
- except Exception as e:
203
- if fallback_env:
204
- raise e
205
- finally:
206
- if client is not None:
207
- client.auth.sign_out(options={"scope": "local"})
208
-
209
- return result
210
-
211
-
212
- def call_with_fallback(
213
- callable,
214
- **kwargs,
215
- ):
216
- for fallback_env in [False, True]:
217
- client = None
218
- try:
219
- client = connect_hub(fallback_env=fallback_env)
220
- result = callable(**kwargs, client=client)
221
- break
222
- except Exception as e:
223
- if fallback_env:
224
- raise e
225
- finally:
226
- if client is not None:
227
- # in case there was sign in
228
- client.auth.sign_out(options={"scope": "local"})
229
- return result
230
-
231
-
232
- @contextmanager
233
- def httpx_client():
234
- client = None
235
- try:
236
- # local is used in tests
237
- if os.environ.get("LAMIN_ENV", "prod") == "local":
238
- from fastapi.testclient import TestClient
239
- from laminhub_rest.main import app
240
-
241
- client = TestClient(app)
242
- else:
243
- transport = RetryTransport(
244
- retry=LogRetry(total=2, backoff_factor=0.2),
245
- transport=httpx.HTTPTransport(verify=True, http2=True, trust_env=True),
246
- )
247
- # first we create a client to build the proxy map from the env variables
248
- # if proxies are set, the default transports will be used
249
- # otherwise the RetryTransport object that we assign below
250
- client = httpx.Client(trust_env=True)
251
- client._transport = transport
252
- yield client
253
- finally:
254
- if client is not None:
255
- client.close()
256
-
257
-
258
- def request_with_auth(
259
- url: str,
260
- method: Literal["get", "post", "put", "delete", "head", "options"],
261
- access_token: str,
262
- renew_token: bool = True,
263
- **kwargs,
264
- ):
265
- headers = kwargs.pop("headers", {})
266
- headers["Authorization"] = f"Bearer {access_token}"
267
- timeout = kwargs.pop("timeout", DEFAULT_TIMEOUT)
268
-
269
- with httpx_client() as client:
270
- make_request = getattr(client, method)
271
- response = make_request(url, headers=headers, timeout=timeout, **kwargs)
272
- status_code = response.status_code
273
- # update access_token and try again if failed
274
- if not (200 <= status_code < 300) and renew_token:
275
- from lamindb_setup import settings
276
-
277
- logger.debug(f"{method} {url} failed: {status_code} {response.text}")
278
-
279
- access_token = get_access_token(
280
- settings.user.email, settings.user.password, settings.user.api_key
281
- )
282
-
283
- settings.user.access_token = access_token
284
- save_user_settings(settings.user)
285
-
286
- headers["Authorization"] = f"Bearer {access_token}"
287
- response = make_request(url, headers=headers, timeout=timeout, **kwargs)
288
- return response
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ from contextlib import contextmanager
6
+ from datetime import datetime
7
+ from typing import Literal
8
+ from urllib.request import urlretrieve
9
+
10
+ import httpx
11
+ from httpx_retries import Retry, RetryTransport
12
+ from lamin_utils import logger
13
+ from supabase import Client, ClientOptions, create_client
14
+
15
+ from ._settings_save import save_user_settings
16
+ from ._settings_store import Connector
17
+
18
+
19
+ def load_fallback_connector() -> Connector:
20
+ url = "https://lamin-site-assets.s3.amazonaws.com/connector.env"
21
+ connector_file, _ = urlretrieve(url)
22
+ return Connector.from_env_file(connector_file, "")
23
+
24
+
25
+ PROD_URL = "https://hub.lamin.ai"
26
+ PROD_ANON_KEY = "sb_publishable_YVa4h8hQ-yBhXpfa2cP39w_PhoLW6Nu"
27
+
28
+
29
+ class Environment:
30
+ def __init__(self, fallback: bool = False):
31
+ lamin_env = os.getenv("LAMIN_ENV")
32
+ if lamin_env is None:
33
+ lamin_env = "prod"
34
+ # set public key
35
+ if lamin_env == "prod":
36
+ if not fallback:
37
+ url = PROD_URL
38
+ key = PROD_ANON_KEY
39
+ else:
40
+ connector = load_fallback_connector()
41
+ url = connector.url
42
+ key = connector.key
43
+ elif lamin_env == "staging":
44
+ url = "https://amvrvdwndlqdzgedrqdv.supabase.co"
45
+ key = "sb_publishable_amVjtilv_Yj4VmGLmxtq6A_sYlLoQx5"
46
+ elif lamin_env == "staging-test":
47
+ url = "https://iugyyajllqftbpidapak.supabase.co"
48
+ key = "sb_publishable_XmXroXqTLQw-eeT5kysCww_k8vJv-4L"
49
+ elif lamin_env == "prod-test":
50
+ url = "https://xtdacpwiqwpbxsatoyrv.supabase.co"
51
+ key = "sb_publishable_G-pyO5aW6VFErTzJyVvM5w_NAv1_Mf7"
52
+ else:
53
+ url = os.environ["SUPABASE_API_URL"]
54
+ key = os.environ["SUPABASE_ANON_KEY"]
55
+ self.lamin_env: str = lamin_env
56
+ self.supabase_api_url: str = url
57
+ self.supabase_anon_key: str = key
58
+
59
+
60
+ DEFAULT_TIMEOUT = 12
61
+
62
+
63
+ # needed to log retries
64
+ class LogRetry(Retry):
65
+ def increment(self):
66
+ new = super().increment()
67
+ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
68
+ # new.attempts_made is the 1-based retry count
69
+ logger.warning(f"{now} HTTP retry attempt {new.attempts_made}/{new.total}")
70
+ return new
71
+
72
+
73
+ # runs ~0.5s
74
+ def connect_hub(
75
+ fallback_env: bool = False, client_options: ClientOptions | None = None
76
+ ) -> Client:
77
+ env = Environment(fallback=fallback_env)
78
+ if client_options is None:
79
+ client_options = ClientOptions(
80
+ auto_refresh_token=False,
81
+ function_client_timeout=DEFAULT_TIMEOUT,
82
+ postgrest_client_timeout=DEFAULT_TIMEOUT,
83
+ )
84
+ client = create_client(env.supabase_api_url, env.supabase_anon_key, client_options)
85
+ # needed to enable retries for http requests in supabase
86
+ # these are separate clients and need separate transports
87
+ transports = []
88
+ for _ in range(2):
89
+ transports.append(
90
+ RetryTransport(
91
+ retry=LogRetry(total=2, backoff_factor=0.2),
92
+ transport=httpx.HTTPTransport(verify=True, http2=True, trust_env=True),
93
+ )
94
+ )
95
+ # this overwrites transports of existing httpx clients
96
+ # if proxies are set, the default transports that were created on clients init
97
+ # will be used, irrespective of these re-settings
98
+ client.auth._http_client._transport = transports[0]
99
+ client.postgrest.session._transport = transports[1]
100
+ # POST is not retryable by default, but for our functions it should be safe to retry
101
+ client.functions._client._transport = RetryTransport(
102
+ retry=LogRetry(
103
+ total=2,
104
+ backoff_factor=0.2,
105
+ allowed_methods=[
106
+ "HEAD",
107
+ "GET",
108
+ "PUT",
109
+ "DELETE",
110
+ "OPTIONS",
111
+ "TRACE",
112
+ "POST",
113
+ ],
114
+ ),
115
+ transport=httpx.HTTPTransport(verify=True, http2=True, trust_env=True),
116
+ )
117
+ return client
118
+
119
+
120
+ def connect_hub_with_auth(
121
+ fallback_env: bool = False,
122
+ renew_token: bool = False,
123
+ access_token: str | None = None,
124
+ ) -> Client:
125
+ hub = connect_hub(fallback_env=fallback_env)
126
+ if access_token is None:
127
+ from lamindb_setup import settings
128
+
129
+ if renew_token:
130
+ settings.user.access_token = get_access_token(
131
+ settings.user.email, settings.user.password, settings.user.api_key
132
+ )
133
+ access_token = settings.user.access_token
134
+ hub.postgrest.auth(access_token)
135
+ hub.functions.set_auth(access_token)
136
+ return hub
137
+
138
+
139
+ # runs ~0.5s
140
+ def get_access_token(
141
+ email: str | None = None, password: str | None = None, api_key: str | None = None
142
+ ):
143
+ hub = connect_hub()
144
+ try:
145
+ if api_key is not None:
146
+ auth_response = hub.functions.invoke(
147
+ "get-jwt-v1",
148
+ invoke_options={"body": {"api_key": api_key}},
149
+ )
150
+ return json.loads(auth_response)["accessToken"]
151
+ auth_response = hub.auth.sign_in_with_password(
152
+ {
153
+ "email": email,
154
+ "password": password,
155
+ }
156
+ )
157
+ return auth_response.session.access_token
158
+ except Exception as e:
159
+ # we need to log the problem here because the exception is usually caught outside
160
+ # in call_with_fallback_auth
161
+ logger.warning(f"failed to update your lamindb access token: {e}")
162
+ raise e
163
+ finally:
164
+ hub.auth.sign_out(options={"scope": "local"})
165
+
166
+
167
+ def call_with_fallback_auth(
168
+ callable,
169
+ **kwargs,
170
+ ):
171
+ access_token = kwargs.pop("access_token", None)
172
+
173
+ if access_token is not None:
174
+ client = None
175
+ try:
176
+ client = connect_hub_with_auth(access_token=access_token)
177
+ result = callable(**kwargs, client=client)
178
+ finally:
179
+ if client is not None:
180
+ client.auth.sign_out(options={"scope": "local"})
181
+
182
+ return result
183
+
184
+ for renew_token, fallback_env in [(False, False), (True, False), (False, True)]:
185
+ client = None
186
+ try:
187
+ client = connect_hub_with_auth(
188
+ renew_token=renew_token, fallback_env=fallback_env
189
+ )
190
+ result = callable(**kwargs, client=client)
191
+ # we update access_token here
192
+ # because at this point the call has been successfully resolved
193
+ if renew_token:
194
+ from lamindb_setup import settings
195
+
196
+ # here settings.user contains an updated access_token
197
+ save_user_settings(settings.user)
198
+ break
199
+ # we use Exception here as the ways in which the client fails upon 401
200
+ # are not consistent and keep changing
201
+ # because we ultimately raise the error, it's OK I'd say
202
+ except Exception as e:
203
+ if fallback_env:
204
+ raise e
205
+ finally:
206
+ if client is not None:
207
+ client.auth.sign_out(options={"scope": "local"})
208
+
209
+ return result
210
+
211
+
212
+ def call_with_fallback(
213
+ callable,
214
+ **kwargs,
215
+ ):
216
+ for fallback_env in [False, True]:
217
+ client = None
218
+ try:
219
+ client = connect_hub(fallback_env=fallback_env)
220
+ result = callable(**kwargs, client=client)
221
+ break
222
+ except Exception as e:
223
+ if fallback_env:
224
+ raise e
225
+ finally:
226
+ if client is not None:
227
+ # in case there was sign in
228
+ client.auth.sign_out(options={"scope": "local"})
229
+ return result
230
+
231
+
232
+ @contextmanager
233
+ def httpx_client():
234
+ client = None
235
+ try:
236
+ # local is used in tests
237
+ if os.environ.get("LAMIN_ENV", "prod") == "local":
238
+ from fastapi.testclient import TestClient
239
+ from laminhub_rest.main import app
240
+
241
+ client = TestClient(app)
242
+ else:
243
+ transport = RetryTransport(
244
+ retry=LogRetry(total=2, backoff_factor=0.2),
245
+ transport=httpx.HTTPTransport(verify=True, http2=True, trust_env=True),
246
+ )
247
+ # first we create a client to build the proxy map from the env variables
248
+ # if proxies are set, the default transports will be used
249
+ # otherwise the RetryTransport object that we assign below
250
+ client = httpx.Client(trust_env=True)
251
+ client._transport = transport
252
+ yield client
253
+ finally:
254
+ if client is not None:
255
+ client.close()
256
+
257
+
258
+ def request_with_auth(
259
+ url: str,
260
+ method: Literal["get", "post", "put", "delete", "head", "options"],
261
+ access_token: str,
262
+ renew_token: bool = True,
263
+ **kwargs,
264
+ ):
265
+ headers = kwargs.pop("headers", {})
266
+ headers["Authorization"] = f"Bearer {access_token}"
267
+ timeout = kwargs.pop("timeout", DEFAULT_TIMEOUT)
268
+
269
+ with httpx_client() as client:
270
+ make_request = getattr(client, method)
271
+ response = make_request(url, headers=headers, timeout=timeout, **kwargs)
272
+ status_code = response.status_code
273
+ # update access_token and try again if failed
274
+ if not (200 <= status_code < 300) and renew_token:
275
+ from lamindb_setup import settings
276
+
277
+ logger.debug(f"{method} {url} failed: {status_code} {response.text}")
278
+
279
+ access_token = get_access_token(
280
+ settings.user.email, settings.user.password, settings.user.api_key
281
+ )
282
+
283
+ settings.user.access_token = access_token
284
+ save_user_settings(settings.user)
285
+
286
+ headers["Authorization"] = f"Bearer {access_token}"
287
+ response = make_request(url, headers=headers, timeout=timeout, **kwargs)
288
+ return response