lamindb_setup 0.78.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. lamindb_setup/__init__.py +74 -0
  2. lamindb_setup/_cache.py +48 -0
  3. lamindb_setup/_check.py +7 -0
  4. lamindb_setup/_check_setup.py +92 -0
  5. lamindb_setup/_close.py +35 -0
  6. lamindb_setup/_connect_instance.py +429 -0
  7. lamindb_setup/_delete.py +141 -0
  8. lamindb_setup/_django.py +39 -0
  9. lamindb_setup/_entry_points.py +22 -0
  10. lamindb_setup/_exportdb.py +68 -0
  11. lamindb_setup/_importdb.py +50 -0
  12. lamindb_setup/_init_instance.py +411 -0
  13. lamindb_setup/_migrate.py +239 -0
  14. lamindb_setup/_register_instance.py +36 -0
  15. lamindb_setup/_schema.py +27 -0
  16. lamindb_setup/_schema_metadata.py +411 -0
  17. lamindb_setup/_set_managed_storage.py +55 -0
  18. lamindb_setup/_setup_user.py +137 -0
  19. lamindb_setup/_silence_loggers.py +44 -0
  20. lamindb_setup/core/__init__.py +21 -0
  21. lamindb_setup/core/_aws_credentials.py +151 -0
  22. lamindb_setup/core/_aws_storage.py +48 -0
  23. lamindb_setup/core/_deprecated.py +55 -0
  24. lamindb_setup/core/_docs.py +14 -0
  25. lamindb_setup/core/_hub_client.py +173 -0
  26. lamindb_setup/core/_hub_core.py +554 -0
  27. lamindb_setup/core/_hub_crud.py +211 -0
  28. lamindb_setup/core/_hub_utils.py +109 -0
  29. lamindb_setup/core/_private_django_api.py +88 -0
  30. lamindb_setup/core/_settings.py +184 -0
  31. lamindb_setup/core/_settings_instance.py +485 -0
  32. lamindb_setup/core/_settings_load.py +117 -0
  33. lamindb_setup/core/_settings_save.py +92 -0
  34. lamindb_setup/core/_settings_storage.py +350 -0
  35. lamindb_setup/core/_settings_store.py +75 -0
  36. lamindb_setup/core/_settings_user.py +55 -0
  37. lamindb_setup/core/_setup_bionty_sources.py +101 -0
  38. lamindb_setup/core/cloud_sqlite_locker.py +237 -0
  39. lamindb_setup/core/django.py +115 -0
  40. lamindb_setup/core/exceptions.py +10 -0
  41. lamindb_setup/core/hashing.py +116 -0
  42. lamindb_setup/core/types.py +17 -0
  43. lamindb_setup/core/upath.py +779 -0
  44. lamindb_setup-0.78.0.dist-info/LICENSE +201 -0
  45. lamindb_setup-0.78.0.dist-info/METADATA +47 -0
  46. lamindb_setup-0.78.0.dist-info/RECORD +47 -0
  47. lamindb_setup-0.78.0.dist-info/WHEEL +4 -0
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+
3
+ from django.urls import path
4
+ from lamin_utils import logger
5
+
6
+ try:
7
+ from schema_graph.views import Schema
8
+ except ImportError:
9
+ logger.error("to view the schema: pip install django-schema-graph")
10
+
11
+
12
+ urlpatterns = [
13
+ path("schema/", Schema.as_view()),
14
+ ]
15
+
16
+
17
+ def view():
18
+ from django.core.management import call_command
19
+
20
+ from ._check_setup import _check_instance_setup
21
+ from .core._settings import settings
22
+ from .core.django import setup_django
23
+
24
+ if _check_instance_setup():
25
+ raise RuntimeError("Restart Python session or use CLI!")
26
+ setup_django(settings.instance, view_schema=True)
27
+ call_command("runserver")
@@ -0,0 +1,411 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import importlib
5
+ import json
6
+ from typing import TYPE_CHECKING, Literal
7
+ from uuid import UUID
8
+
9
+ from django.db.models import (
10
+ Field,
11
+ ForeignKey,
12
+ ForeignObjectRel,
13
+ ManyToManyField,
14
+ ManyToManyRel,
15
+ ManyToOneRel,
16
+ OneToOneField,
17
+ OneToOneRel,
18
+ )
19
+ from pydantic import BaseModel
20
+
21
+ from lamindb_setup import settings
22
+ from lamindb_setup._init_instance import get_schema_module_name
23
+ from lamindb_setup.core._hub_client import call_with_fallback_auth
24
+
25
+ # surpress pydantic warning about `model_` namespace
26
+ try:
27
+ BaseModel.model_config["protected_namespaces"] = ()
28
+ except Exception:
29
+ pass
30
+
31
+
32
+ if TYPE_CHECKING:
33
+ from supabase import Client
34
+
35
+
36
+ def update_schema_in_hub(access_token: str | None = None) -> tuple[bool, UUID, dict]:
37
+ return call_with_fallback_auth(_synchronize_schema, access_token=access_token)
38
+
39
+
40
+ def _synchronize_schema(client: Client) -> tuple[bool, UUID, dict]:
41
+ schema_metadata = _SchemaHandler()
42
+ schema_metadata_dict = schema_metadata.to_json()
43
+ schema_uuid = _dict_to_uuid(schema_metadata_dict)
44
+ schema = _get_schema_by_id(schema_uuid, client)
45
+
46
+ is_new = schema is None
47
+ if is_new:
48
+ module_set_info = schema_metadata._get_module_set_info()
49
+ module_ids = "-".join(str(module_info["id"]) for module_info in module_set_info)
50
+ schema = (
51
+ client.table("schema")
52
+ .insert(
53
+ {
54
+ "id": schema_uuid.hex,
55
+ "module_ids": module_ids,
56
+ "module_set_info": module_set_info,
57
+ "schema_json": schema_metadata_dict,
58
+ }
59
+ )
60
+ .execute()
61
+ .data[0]
62
+ )
63
+
64
+ instance_response = (
65
+ client.table("instance")
66
+ .update({"schema_id": schema_uuid.hex})
67
+ .eq("id", settings.instance._id.hex)
68
+ .execute()
69
+ )
70
+ assert (
71
+ len(instance_response.data) == 1
72
+ ), f"schema of instance {settings.instance._id.hex} could not be updated with schema {schema_uuid.hex}"
73
+
74
+ return is_new, schema_uuid, schema
75
+
76
+
77
+ def get_schema_by_id(id: UUID):
78
+ return call_with_fallback_auth(_get_schema_by_id, id=id)
79
+
80
+
81
+ def _get_schema_by_id(id: UUID, client: Client):
82
+ response = client.table("schema").select("*").eq("id", id.hex).execute()
83
+ if len(response.data) == 0:
84
+ return None
85
+ return response.data[0]
86
+
87
+
88
+ def _dict_to_uuid(dict: dict):
89
+ encoded = json.dumps(dict, sort_keys=True).encode("utf-8")
90
+ hash = hashlib.md5(encoded).digest()
91
+ uuid = UUID(bytes=hash[:16])
92
+ return uuid
93
+
94
+
95
+ RelationType = Literal["many-to-one", "one-to-many", "many-to-many", "one-to-one"]
96
+ Type = Literal[
97
+ "ForeignKey",
98
+ # the following are generated with `from django.db import models; [attr for attr in dir(models) if attr.endswith('Field')]`
99
+ "AutoField",
100
+ "BigAutoField",
101
+ "BigIntegerField",
102
+ "BinaryField",
103
+ "BooleanField",
104
+ "CharField",
105
+ "CommaSeparatedIntegerField",
106
+ "DateField",
107
+ "DateTimeField",
108
+ "DecimalField",
109
+ "DurationField",
110
+ "EmailField",
111
+ "Field",
112
+ "FileField",
113
+ "FilePathField",
114
+ "FloatField",
115
+ "GeneratedField",
116
+ "GenericIPAddressField",
117
+ "IPAddressField",
118
+ "ImageField",
119
+ "IntegerField",
120
+ "JSONField",
121
+ "ManyToManyField",
122
+ "NullBooleanField",
123
+ "OneToOneField",
124
+ "PositiveBigIntegerField",
125
+ "PositiveIntegerField",
126
+ "PositiveSmallIntegerField",
127
+ "SlugField",
128
+ "SmallAutoField",
129
+ "SmallIntegerField",
130
+ "TextField",
131
+ "TimeField",
132
+ "URLField",
133
+ "UUIDField",
134
+ ]
135
+
136
+
137
+ class Through(BaseModel):
138
+ left_key: str
139
+ right_key: str
140
+ link_table_name: str | None = None
141
+
142
+
143
+ class FieldMetadata(BaseModel):
144
+ type: Type
145
+ column_name: str | None = None
146
+ through: Through | None = None
147
+ field_name: str
148
+ model_name: str
149
+ schema_name: str
150
+ is_link_table: bool
151
+ relation_type: RelationType | None = None
152
+ related_field_name: str | None = None
153
+ related_model_name: str | None = None
154
+ related_schema_name: str | None = None
155
+
156
+
157
+ class _ModelHandler:
158
+ def __init__(self, model, module_name: str, included_modules: list[str]) -> None:
159
+ self.model = model
160
+ self.class_name = model.__name__
161
+ self.module_name = module_name
162
+ self.model_name = model._meta.model_name
163
+ self.table_name = model._meta.db_table
164
+ self.included_modules = included_modules
165
+ self.fields = self._get_fields_metadata(self.model)
166
+
167
+ def to_dict(self, include_django_objects: bool = True):
168
+ _dict = {
169
+ "fields": self.fields.copy(),
170
+ "class_name": self.class_name,
171
+ "table_name": self.table_name,
172
+ }
173
+
174
+ for field_name in self.fields.keys():
175
+ _dict["fields"][field_name] = _dict["fields"][field_name].__dict__
176
+ through = _dict["fields"][field_name]["through"]
177
+ if through is not None:
178
+ _dict["fields"][field_name]["through"] = through.__dict__
179
+
180
+ if include_django_objects:
181
+ _dict.update({"model": self.model})
182
+
183
+ return _dict
184
+
185
+ def _get_fields_metadata(self, model):
186
+ related_fields = []
187
+ fields_metadata: dict[str, FieldMetadata] = {}
188
+
189
+ for field in model._meta.get_fields():
190
+ field_metadata = self._get_field_metadata(model, field)
191
+ if field_metadata.related_schema_name is None:
192
+ fields_metadata.update({field.name: field_metadata})
193
+
194
+ if (
195
+ field_metadata.related_schema_name in self.included_modules
196
+ and field_metadata.schema_name in self.included_modules
197
+ ):
198
+ related_fields.append(field)
199
+
200
+ related_fields_metadata = self._get_related_fields_metadata(
201
+ model, related_fields
202
+ )
203
+
204
+ fields_metadata = {**fields_metadata, **related_fields_metadata}
205
+
206
+ return fields_metadata
207
+
208
+ def _get_related_fields_metadata(self, model, fields: list[ForeignObjectRel]):
209
+ related_fields: dict[str, FieldMetadata] = {}
210
+
211
+ for field in fields:
212
+ if field.many_to_one:
213
+ related_fields.update(
214
+ {f"{field.name}": self._get_field_metadata(model, field)}
215
+ )
216
+ elif field.one_to_many:
217
+ # exclude self reference as it is already included in the many to one
218
+ if field.related_model == model:
219
+ continue
220
+ related_fields.update(
221
+ {f"{field.name}": self._get_field_metadata(model, field.field)}
222
+ )
223
+ elif field.many_to_many:
224
+ related_fields.update(
225
+ {f"{field.name}": self._get_field_metadata(model, field)}
226
+ )
227
+ elif field.one_to_one:
228
+ related_fields.update(
229
+ {f"{field.name}": self._get_field_metadata(model, field)}
230
+ )
231
+
232
+ return related_fields
233
+
234
+ def _get_field_metadata(self, model, field: Field):
235
+ from lnschema_core.models import LinkORM
236
+
237
+ internal_type = field.get_internal_type()
238
+ model_name = field.model._meta.model_name
239
+ relation_type = self._get_relation_type(model, field)
240
+ if field.related_model is None:
241
+ schema_name = field.model.__get_schema_name__()
242
+ related_model_name = None
243
+ related_schema_name = None
244
+ related_field_name = None
245
+ field_name = field.name
246
+ else:
247
+ related_model_name = field.related_model._meta.model_name
248
+ related_schema_name = field.related_model.__get_schema_name__()
249
+ schema_name = field.model.__get_schema_name__()
250
+ related_field_name = field.remote_field.name
251
+ field_name = field.name
252
+
253
+ if relation_type in ["one-to-many"]:
254
+ # For a one-to-many relation, the field belong
255
+ # to the other model as a foreign key.
256
+ # To make usage similar to other relation types
257
+ # we need to invert model and related model.
258
+ schema_name, related_schema_name = related_schema_name, schema_name
259
+ model_name, related_model_name = related_model_name, model_name
260
+ field_name, related_field_name = related_field_name, field_name
261
+ pass
262
+
263
+ column = None
264
+ if relation_type not in ["many-to-many", "one-to-many"]:
265
+ if not isinstance(field, ForeignObjectRel):
266
+ column = field.column
267
+
268
+ if relation_type is None:
269
+ through = None
270
+ elif relation_type == "many-to-many":
271
+ through = self._get_through_many_to_many(field)
272
+ else:
273
+ through = self._get_through(field)
274
+
275
+ return FieldMetadata(
276
+ schema_name=schema_name,
277
+ model_name=model_name,
278
+ field_name=field_name,
279
+ type=internal_type,
280
+ is_link_table=issubclass(field.model, LinkORM),
281
+ column_name=column,
282
+ relation_type=relation_type,
283
+ related_schema_name=related_schema_name,
284
+ related_model_name=related_model_name,
285
+ related_field_name=related_field_name,
286
+ through=through,
287
+ )
288
+
289
+ @staticmethod
290
+ def _get_through_many_to_many(field_or_rel: ManyToManyField | ManyToManyRel):
291
+ from lnschema_core.models import Registry
292
+
293
+ if isinstance(field_or_rel, ManyToManyField):
294
+ if field_or_rel.model != Registry:
295
+ return Through(
296
+ left_key=field_or_rel.m2m_column_name(),
297
+ right_key=field_or_rel.m2m_reverse_name(),
298
+ link_table_name=field_or_rel.remote_field.through._meta.db_table,
299
+ )
300
+ else:
301
+ return Through(
302
+ left_key=field_or_rel.m2m_reverse_name(),
303
+ right_key=field_or_rel.m2m_column_name(),
304
+ link_table_name=field_or_rel.remote_field.through._meta.db_table,
305
+ )
306
+
307
+ if isinstance(field_or_rel, ManyToManyRel):
308
+ if field_or_rel.model != Registry:
309
+ return Through(
310
+ left_key=field_or_rel.field.m2m_reverse_name(),
311
+ right_key=field_or_rel.field.m2m_column_name(),
312
+ link_table_name=field_or_rel.through._meta.db_table,
313
+ )
314
+ else:
315
+ return Through(
316
+ left_key=field_or_rel.field.m2m_column_name(),
317
+ right_key=field_or_rel.field.m2m_reverse_name(),
318
+ link_table_name=field_or_rel.through._meta.db_table,
319
+ )
320
+
321
+ def _get_through(
322
+ self, field_or_rel: ForeignKey | OneToOneField | ManyToOneRel | OneToOneRel
323
+ ):
324
+ if isinstance(field_or_rel, ForeignObjectRel):
325
+ rel_1 = field_or_rel.field.related_fields[0][0]
326
+ rel_2 = field_or_rel.field.related_fields[0][1]
327
+ else:
328
+ rel_1 = field_or_rel.related_fields[0][0]
329
+ rel_2 = field_or_rel.related_fields[0][1]
330
+
331
+ if rel_1.model._meta.model_name == self.model._meta.model_name:
332
+ return Through(
333
+ left_key=rel_1.column,
334
+ right_key=rel_2.column,
335
+ )
336
+ else:
337
+ return Through(
338
+ left_key=rel_2.column,
339
+ right_key=rel_1.column,
340
+ )
341
+
342
+ @staticmethod
343
+ def _get_relation_type(model, field: Field):
344
+ if field.many_to_one:
345
+ # defined in the model
346
+ if model == field.model:
347
+ return "many-to-one"
348
+ # defined in the related model
349
+ else:
350
+ return "one-to-many"
351
+ elif field.one_to_many:
352
+ return "one-to-many"
353
+ elif field.many_to_many:
354
+ return "many-to-many"
355
+ elif field.one_to_one:
356
+ return "one-to-one"
357
+ else:
358
+ return None
359
+
360
+
361
+ class _SchemaHandler:
362
+ def __init__(self) -> None:
363
+ self.included_modules = ["core"] + list(settings.instance.schema)
364
+ self.modules = self._get_modules_metadata()
365
+
366
+ def to_dict(self, include_django_objects: bool = True):
367
+ return {
368
+ module_name: {
369
+ model_name: model.to_dict(include_django_objects)
370
+ for model_name, model in module.items()
371
+ }
372
+ for module_name, module in self.modules.items()
373
+ }
374
+
375
+ def to_json(self):
376
+ return self.to_dict(include_django_objects=False)
377
+
378
+ def _get_modules_metadata(self):
379
+ from lnschema_core.models import Record, Registry
380
+
381
+ all_models = {
382
+ module_name: {
383
+ model._meta.model_name: _ModelHandler(
384
+ model, module_name, self.included_modules
385
+ )
386
+ for model in self._get_schema_module(
387
+ module_name
388
+ ).models.__dict__.values()
389
+ if model.__class__ is Registry
390
+ and model is not Record
391
+ and not model._meta.abstract
392
+ and model.__get_schema_name__() == module_name
393
+ }
394
+ for module_name in self.included_modules
395
+ }
396
+ assert all_models
397
+ return all_models
398
+
399
+ def _get_module_set_info(self):
400
+ # TODO: rely on schemamodule table for this
401
+ module_set_info = []
402
+ for module_name in self.included_modules:
403
+ module = self._get_schema_module(module_name)
404
+ module_set_info.append(
405
+ {"id": 0, "name": module_name, "version": module.__version__}
406
+ )
407
+ return module_set_info
408
+
409
+ @staticmethod
410
+ def _get_schema_module(module_name):
411
+ return importlib.import_module(get_schema_module_name(module_name))
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import TYPE_CHECKING
4
+
5
+ from lamin_utils import logger
6
+
7
+ from ._init_instance import register_storage_in_instance
8
+ from .core._hub_core import delete_storage_record
9
+ from .core._settings import settings
10
+ from .core._settings_storage import init_storage
11
+
12
+ if TYPE_CHECKING:
13
+ from lamindb_setup.core.types import UPathStr
14
+
15
+
16
+ def set_managed_storage(root: UPathStr, **fs_kwargs):
17
+ """Add or switch to another managed storage location.
18
+
19
+ Args:
20
+ root: `UPathStr` - The new storage root, e.g., an S3 bucket.
21
+ **fs_kwargs: Additional fsspec arguments for cloud root, e.g., profile.
22
+
23
+ """
24
+ if settings.instance.dialect == "sqlite":
25
+ raise ValueError(
26
+ "Can't add additional managed storage locations for sqlite instances."
27
+ )
28
+ if not settings.instance.is_on_hub:
29
+ raise ValueError(
30
+ "Can't add additional managed storage locations for instances that aren't managed through the hub."
31
+ )
32
+ # here the storage is registered in the hub
33
+ # hub_record_status="hub-record-created" if a new record is created
34
+ # "hub-record-retireved" if the storage is in the hub already
35
+ ssettings, hub_record_status = init_storage(
36
+ root=root, instance_id=settings.instance._id, register_hub=True
37
+ )
38
+ if ssettings._instance_id is None:
39
+ raise ValueError(
40
+ f"Cannot manage storage without write access: {ssettings.root}"
41
+ )
42
+
43
+ # here the storage is saved in the instance
44
+ # if any error happens the record in the hub is deleted
45
+ # if it was created earlier and not retrieved
46
+ try:
47
+ register_storage_in_instance(ssettings)
48
+ except Exception as e:
49
+ if hub_record_status == "hub-record-created" and ssettings._uuid is not None:
50
+ delete_storage_record(ssettings._uuid) # type: ignore
51
+ raise e
52
+
53
+ settings.instance._storage = ssettings
54
+ settings.instance._persist() # this also updates the settings object
55
+ settings.storage._set_fs_kwargs(**fs_kwargs)
@@ -0,0 +1,137 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import TYPE_CHECKING
5
+
6
+ from lamin_utils import logger
7
+
8
+ from ._check_setup import _check_instance_setup
9
+ from ._init_instance import register_user
10
+ from .core._settings import settings
11
+ from .core._settings_load import load_or_create_user_settings, load_user_settings
12
+ from .core._settings_save import save_user_settings
13
+ from .core._settings_store import (
14
+ current_user_settings_file,
15
+ user_settings_file_email,
16
+ user_settings_file_handle,
17
+ )
18
+
19
+ if TYPE_CHECKING:
20
+ from lamindb_setup.core._settings_save import UserSettings
21
+
22
+
23
+ def load_user(email: str | None = None, handle: str | None = None) -> UserSettings:
24
+ if email is not None:
25
+ settings_file = user_settings_file_email(email)
26
+ if handle is not None:
27
+ settings_file = user_settings_file_handle(handle)
28
+ if settings_file.exists():
29
+ user_settings = load_user_settings(settings_file)
30
+ save_user_settings(user_settings) # needed to save to current_user.env
31
+ assert user_settings.email is not None or user_settings.api_key is not None
32
+ else:
33
+ user_settings = load_or_create_user_settings()
34
+ if email is None:
35
+ raise SystemExit(
36
+ "✗ Use your email for your first login in a compute environment. "
37
+ "After that, you can use your handle."
38
+ )
39
+ user_settings.email = email
40
+ user_settings.handle = handle
41
+ save_user_settings(user_settings)
42
+
43
+ from .core._settings import settings
44
+
45
+ settings._user_settings = None # this is to refresh a settings instance
46
+
47
+ return user_settings
48
+
49
+
50
+ def login(
51
+ user: str | None = None, *, key: str | None = None, api_key: str | None = None
52
+ ) -> None:
53
+ """Log in user.
54
+
55
+ Args:
56
+ user: handle or email
57
+ key: API key
58
+ api_key: Beta API key
59
+ """
60
+ if user is None and api_key is None:
61
+ if "LAMIN_API_KEY" in os.environ:
62
+ api_key = os.environ["LAMIN_API_KEY"]
63
+ else:
64
+ raise ValueError("Both `user` and `api_key` should not be `None`.")
65
+
66
+ if api_key is None:
67
+ if "@" in user: # type: ignore
68
+ email, handle = user, None
69
+ else:
70
+ email, handle = None, user
71
+ user_settings = load_user(email, handle)
72
+
73
+ if key is not None:
74
+ # within UserSettings, we still call it "password" for a while
75
+ user_settings.password = key
76
+
77
+ if user_settings.password is None:
78
+ api_key = user_settings.api_key
79
+ if api_key is None:
80
+ raise SystemExit(
81
+ "✗ No stored API key, please call: "
82
+ "`lamin login` or `lamin login <your-email> --key <API-key>`"
83
+ )
84
+ elif user_settings.email is None:
85
+ raise SystemExit(f"✗ No stored user email, please call: lamin login {user}")
86
+ else:
87
+ user_settings = load_or_create_user_settings()
88
+
89
+ from .core._hub_core import sign_in_hub, sign_in_hub_api_key
90
+
91
+ if api_key is None:
92
+ response = sign_in_hub(
93
+ user_settings.email, # type: ignore
94
+ user_settings.password, # type: ignore
95
+ user_settings.handle,
96
+ )
97
+ else:
98
+ response = sign_in_hub_api_key(api_key)
99
+ user_settings.password = None
100
+
101
+ if isinstance(response, Exception):
102
+ raise response
103
+ elif isinstance(response, str):
104
+ raise SystemExit(f"✗ Unsuccessful login: {response}.")
105
+ else:
106
+ user_uuid, user_id, user_handle, user_name, access_token = response
107
+ if api_key is not None:
108
+ logger.success(
109
+ f"logged in with API key (handle: {user_handle}, uid: {user_id})"
110
+ )
111
+ elif handle is None:
112
+ logger.success(f"logged in with handle {user_handle} (uid: {user_id})")
113
+ else:
114
+ logger.success(f"logged in with email {user_settings.email} (uid: {user_id})")
115
+ user_settings.uid = user_id
116
+ user_settings.handle = user_handle
117
+ user_settings.name = user_name
118
+ user_settings._uuid = user_uuid
119
+ user_settings.access_token = access_token
120
+ user_settings.api_key = api_key
121
+ save_user_settings(user_settings)
122
+
123
+ if settings._instance_exists and _check_instance_setup():
124
+ register_user(user_settings)
125
+
126
+ settings._user_settings = None
127
+ return None
128
+
129
+
130
+ def logout():
131
+ if current_user_settings_file().exists():
132
+ current_user_settings_file().unlink()
133
+ # update user info
134
+ settings._user_settings = None
135
+ logger.success("logged out")
136
+ else:
137
+ logger.important("already logged out")
@@ -0,0 +1,44 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+
5
+ silenced = False
6
+
7
+
8
+ # from https://github.com/boto/boto3/blob/8c6e641bed8130a9d8cb4d97b4acbe7aa0d0657a/boto3/__init__.py#L37
9
+ def set_stream_logger(name, level):
10
+ logger = logging.getLogger(name)
11
+ logger.setLevel(level)
12
+ handler = logging.StreamHandler()
13
+ handler.setLevel(level)
14
+ logger.addHandler(handler)
15
+
16
+
17
+ def silence_loggers():
18
+ global silenced
19
+
20
+ if not silenced:
21
+ # this gets logged 6 times
22
+ set_stream_logger(name="botocore.credentials", level=logging.WARNING)
23
+ set_stream_logger(name="botocore.hooks", level=logging.WARNING)
24
+ set_stream_logger(name="botocore.utils", level=logging.WARNING)
25
+ set_stream_logger(name="botocore.auth", level=logging.WARNING)
26
+ set_stream_logger(name="botocore.endpoint", level=logging.WARNING)
27
+ set_stream_logger(name="httpx", level=logging.WARNING)
28
+ try:
29
+ import aiobotocore
30
+
31
+ # the 7th logging message of credentials came from aiobotocore
32
+ set_stream_logger(name="aiobotocore.credentials", level=logging.WARNING)
33
+ except ImportError:
34
+ pass
35
+ try:
36
+ # google also aggressively logs authentication related warnings
37
+ # in cases where users access public data
38
+ set_stream_logger(name="google.auth._default", level=logging.ERROR)
39
+ set_stream_logger(
40
+ name="google.auth.compute_engine._metadata", level=logging.ERROR
41
+ )
42
+ except Exception:
43
+ pass
44
+ silenced = True
@@ -0,0 +1,21 @@
1
+ """Core setup library.
2
+
3
+ Settings:
4
+
5
+ .. autosummary::
6
+ :toctree:
7
+
8
+ SetupSettings
9
+ UserSettings
10
+ InstanceSettings
11
+ StorageSettings
12
+
13
+ """
14
+
15
+ from . import django, types, upath
16
+ from ._deprecated import deprecated
17
+ from ._docs import doc_args
18
+ from ._settings import SetupSettings
19
+ from ._settings_instance import InstanceSettings
20
+ from ._settings_storage import StorageSettings
21
+ from ._settings_user import UserSettings