lamindb_setup 1.16.0__py3-none-any.whl → 1.18.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.
lamindb_setup/__init__.py CHANGED
@@ -35,14 +35,17 @@ Migration management
35
35
 
36
36
  """
37
37
 
38
- __version__ = "1.16.0" # denote a release candidate for 0.1.0 with 0.1rc1
38
+ __version__ = "1.18.0" # denote a release candidate for 0.1.0 with 0.1rc1
39
39
 
40
40
  import os
41
41
  import warnings
42
42
 
43
- # ignore for now, remove this after supabase upgrade in the deps
43
+ # ignore for now, this is for timeout parameter,
44
+ # it is more convenient to specify it directly for now
44
45
  warnings.filterwarnings("ignore", category=DeprecationWarning, module="supabase")
45
- warnings.filterwarnings("ignore", category=DeprecationWarning, module="supafunc")
46
+ warnings.filterwarnings(
47
+ "ignore", category=DeprecationWarning, module="supabase_functions"
48
+ )
46
49
  warnings.filterwarnings("ignore", category=DeprecationWarning, module="postgrest")
47
50
 
48
51
  from packaging import version as packaging_version
@@ -4,7 +4,6 @@ import functools
4
4
  import importlib as il
5
5
  import inspect
6
6
  import os
7
- from importlib.metadata import distributions
8
7
  from typing import TYPE_CHECKING
9
8
  from uuid import UUID
10
9
 
@@ -16,7 +15,6 @@ from .core._settings import settings
16
15
  from .core._settings_store import current_instance_settings_file
17
16
  from .errors import (
18
17
  MODULE_WASNT_CONFIGURED_MESSAGE_TEMPLATE,
19
- InstanceNotSetupError,
20
18
  ModuleWasntConfigured,
21
19
  )
22
20
 
@@ -26,8 +24,6 @@ if TYPE_CHECKING:
26
24
  from .core._settings_instance import InstanceSettings
27
25
 
28
26
 
29
- CURRENT_ISETTINGS: InstanceSettings | None = None
30
- MODULE_CANDIDATES: set[str] | None = None
31
27
  IS_LOADING: bool = False
32
28
 
33
29
 
@@ -45,55 +41,6 @@ def disable_auto_connect(func: Callable):
45
41
  return wrapper
46
42
 
47
43
 
48
- def find_module_candidates():
49
- """Find all local packages that depend on lamindb."""
50
- global MODULE_CANDIDATES
51
- if MODULE_CANDIDATES is not None:
52
- return MODULE_CANDIDATES
53
- all_dists = list(distributions())
54
- lamindb_deps = {
55
- dist.metadata["Name"].lower()
56
- for dist in all_dists
57
- if dist.requires and any("lamindb" in req.lower() for req in dist.requires)
58
- }
59
- lamindb_deps.remove("lamindb")
60
- MODULE_CANDIDATES = lamindb_deps
61
- return lamindb_deps
62
-
63
-
64
- def _get_current_instance_settings(from_module: str | None = None) -> InstanceSettings:
65
- from .core._settings_instance import InstanceSettings
66
-
67
- global CURRENT_ISETTINGS
68
-
69
- if CURRENT_ISETTINGS is not None:
70
- return CURRENT_ISETTINGS
71
- if current_instance_settings_file().exists():
72
- from .core._settings_load import load_instance_settings
73
-
74
- try:
75
- isettings = load_instance_settings()
76
- except Exception as e:
77
- # user will get more detailed traceback once they run the CLI
78
- logger.error(
79
- "Current instance cannot be reached, disconnect from it: `lamin disconnect`\n"
80
- "Alternatively, init or load a connectable instance on the"
81
- " command line: `lamin connect <instance>` or `lamin init <...>`"
82
- )
83
- raise e
84
- else:
85
- module_candidates = find_module_candidates()
86
- isettings = InstanceSettings(
87
- id=UUID("00000000-0000-0000-0000-000000000000"),
88
- owner="none",
89
- name="none",
90
- storage=None,
91
- modules=",".join(module_candidates),
92
- )
93
- CURRENT_ISETTINGS = isettings
94
- return isettings
95
-
96
-
97
44
  def _normalize_module_name(module_name: str) -> str:
98
45
  return module_name.replace("lnschema_", "").replace("_", "-")
99
46
 
@@ -137,12 +84,9 @@ def _infer_callers_module_name() -> str | None:
137
84
  # users should not see it
138
85
  def _check_instance_setup(from_module: str | None = None) -> bool:
139
86
  if django_lamin.IS_SETUP:
140
- # reload logic here because module might not yet have been imported
141
- # upon first setup
142
87
  if from_module is not None:
143
88
  if from_module != "lamindb":
144
89
  _check_module_in_instance_modules(from_module)
145
- il.reload(il.import_module(from_module))
146
90
  else:
147
91
  infer_module = _infer_callers_module_name()
148
92
  if infer_module is not None and infer_module not in {
@@ -159,34 +103,29 @@ def _check_instance_setup(from_module: str | None = None) -> bool:
159
103
  "errors in regular lamindb usage"
160
104
  )
161
105
  return True
162
- isettings = _get_current_instance_settings()
163
- if isettings is not None:
164
- if from_module is not None and not django_lamin.IS_SETUP and not IS_LOADING:
165
- if from_module != "lamindb":
166
- _check_module_in_instance_modules(from_module, isettings)
167
-
168
- import lamindb
169
-
170
- il.reload(il.import_module(from_module))
171
- else:
172
- django_lamin.setup_django(isettings)
173
- if isettings.slug != "none/none":
174
- logger.important(f"connected lamindb: {isettings.slug}")
175
- # update of local storage location through search_local_root()
176
- settings._instance_settings = isettings
177
- else:
178
- logger.warning("not connected, call: ln.connect('account/name')")
106
+
107
+ if IS_LOADING or from_module is None:
108
+ return False
109
+
110
+ if (
111
+ not settings._instance_exists
112
+ and os.environ.get("LAMIN_CURRENT_INSTANCE") is not None
113
+ ):
114
+ from ._connect_instance import connect
115
+
116
+ connect(_write_settings=False, _reload_lamindb=False)
179
117
  return django_lamin.IS_SETUP
180
118
  else:
181
- if from_module is not None:
182
- # the below enables users to auto-connect to an instance
183
- # simply by setting an environment variable, bypassing the
184
- # need of calling connect() manually
185
- if os.environ.get("LAMIN_CURRENT_INSTANCE") is not None:
186
- from ._connect_instance import connect
187
-
188
- connect(_write_settings=False, _reload_lamindb=False)
189
- return django_lamin.IS_SETUP
190
- else:
191
- logger.warning(InstanceNotSetupError.default_message)
192
- return False
119
+ isettings = settings.instance
120
+ if from_module != "lamindb":
121
+ _check_module_in_instance_modules(from_module, isettings)
122
+
123
+ import lamindb # connect to the instance
124
+ else:
125
+ # disable_auto_connect to avoid triggering _check_instance_setup in modules
126
+ disable_auto_connect(django_lamin.setup_django)(isettings)
127
+ if isettings.slug != "none/none":
128
+ logger.important(f"connected lamindb: {isettings.slug}")
129
+ # update of local storage location through search_local_root()
130
+ settings._instance_settings = isettings
131
+ return django_lamin.IS_SETUP
@@ -9,10 +9,7 @@ from uuid import UUID
9
9
 
10
10
  from lamin_utils import logger
11
11
 
12
- from ._check_setup import (
13
- _check_instance_setup,
14
- _get_current_instance_settings,
15
- )
12
+ from ._check_setup import _check_instance_setup
16
13
  from ._disconnect import disconnect
17
14
  from ._init_instance import load_from_isettings
18
15
  from ._silence_loggers import silence_loggers
@@ -25,7 +22,7 @@ from .core._settings_storage import StorageSettings
25
22
  from .core._settings_store import instance_settings_file
26
23
  from .core.cloud_sqlite_locker import unlock_cloud_sqlite_upon_exception
27
24
  from .core.django import reset_django
28
- from .errors import CannotSwitchDefaultInstance
25
+ from .errors import CannotSwitchDefaultInstance, InstanceNotFoundError
29
26
 
30
27
  if TYPE_CHECKING:
31
28
  from pathlib import Path
@@ -36,8 +33,6 @@ if TYPE_CHECKING:
36
33
  # this is for testing purposes only
37
34
  # set to True only to test failed load
38
35
  _TEST_FAILED_LOAD = False
39
-
40
-
41
36
  INSTANCE_NOT_FOUND_MESSAGE = (
42
37
  "'{owner}/{name}' not found:"
43
38
  " '{hub_result}'\nCheck your permissions:"
@@ -45,10 +40,6 @@ INSTANCE_NOT_FOUND_MESSAGE = (
45
40
  )
46
41
 
47
42
 
48
- class InstanceNotFoundError(SystemExit):
49
- pass
50
-
51
-
52
43
  def check_db_dsn_equal_up_to_credentials(db_dsn_hub, db_dsn_local):
53
44
  return (
54
45
  db_dsn_hub.scheme == db_dsn_local.scheme
@@ -105,6 +96,7 @@ def _connect_instance(
105
96
  use_root_db_user: bool = False,
106
97
  use_proxy_db: bool = False,
107
98
  access_token: str | None = None,
99
+ raise_systemexit: bool = False,
108
100
  ) -> InstanceSettings:
109
101
  settings_file = instance_settings_file(name, owner)
110
102
  make_hub_request = True
@@ -173,12 +165,17 @@ def _connect_instance(
173
165
  )
174
166
  else:
175
167
  message = "It is not possible to load an anonymous-owned instance from the hub"
168
+ exception = (
169
+ SystemExit(message)
170
+ if raise_systemexit
171
+ else InstanceNotFoundError(message)
172
+ )
176
173
  if settings_file.exists():
177
174
  isettings = load_instance_settings(settings_file)
178
175
  if isettings.is_remote:
179
- raise InstanceNotFoundError(message)
176
+ raise exception
180
177
  else:
181
- raise InstanceNotFoundError(message)
178
+ raise exception
182
179
  return isettings
183
180
 
184
181
 
@@ -242,7 +239,11 @@ def _connect_cli(
242
239
 
243
240
  owner, name = get_owner_name_from_identifier(instance)
244
241
  isettings = _connect_instance(
245
- owner, name, use_root_db_user=use_root_db_user, use_proxy_db=use_proxy_db
242
+ owner,
243
+ name,
244
+ use_root_db_user=use_root_db_user,
245
+ use_proxy_db=use_proxy_db,
246
+ raise_systemexit=True,
246
247
  )
247
248
  isettings._persist(write_to_disk=True)
248
249
  if not isettings.is_on_hub or isettings._is_cloud_sqlite:
@@ -266,13 +267,8 @@ def validate_connection_state(
266
267
  from django.db import connection
267
268
 
268
269
  if (
269
- settings._instance_exists
270
+ settings._instance_exists # exists only for real instances, not for none/none
270
271
  and f"{owner}/{name}" == settings.instance.slug
271
- # below is to ensure that if another process interferes
272
- # we don't use the in-memory mock database
273
- # could be made more specific by checking whether the django
274
- # configured database is the same as the one in settings
275
- and connection.settings_dict["NAME"] != ":memory:"
276
272
  and not use_root_db_user # always re-connect for root db user
277
273
  ):
278
274
  logger.important(
@@ -280,7 +276,7 @@ def validate_connection_state(
280
276
  )
281
277
  return None
282
278
  else:
283
- if settings._instance_exists and settings.instance.slug != "none/none":
279
+ if settings._instance_exists:
284
280
  import lamindb as ln
285
281
 
286
282
  if ln.context.transform is not None:
@@ -292,7 +288,9 @@ def validate_connection_state(
292
288
 
293
289
  @unlock_cloud_sqlite_upon_exception(ignore_prev_locker=True)
294
290
  def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
295
- """Connect to an instance.
291
+ """Connect the global default instance.
292
+
293
+ If you want to create a read-only database client, use :class:`~lamindb.DB` instead.
296
294
 
297
295
  Args:
298
296
  instance: Pass a slug (`account/name`) or URL (`https://lamin.ai/account/name`).
@@ -340,12 +338,9 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
340
338
  if settings._instance_exists:
341
339
  isettings = settings.instance
342
340
  else:
343
- isettings_or_none = _get_current_instance_settings()
344
- if isettings_or_none is None:
345
- raise ValueError(
346
- "No instance was connected through the CLI, pass a value to `instance` or connect via the CLI."
347
- )
348
- isettings = isettings_or_none
341
+ raise ValueError(
342
+ "No instance was connected through the CLI, pass a value to `instance` or connect via the CLI."
343
+ )
349
344
  if use_root_db_user:
350
345
  reset_django()
351
346
  owner, name = isettings.owner, isettings.name
@@ -414,7 +409,6 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
414
409
 
415
410
  load_from_isettings(isettings, user=_user, write_settings=_write_settings)
416
411
  if _reload_lamindb:
417
- importlib.reload(importlib.import_module("lamindb"))
418
412
  reset_django_module_variables()
419
413
  if isettings.slug != "none/none":
420
414
  logger.important(f"connected lamindb: {isettings.slug}")
@@ -424,10 +418,6 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
424
418
  isettings._get_settings_file().unlink(missing_ok=True) # type: ignore
425
419
  settings._instance_settings = None
426
420
  raise e
427
- if settings.dev_dir is None:
428
- logger.important_hint(
429
- "to map a local dev directory, set: ln.setup.settings.dev_dir = '.'"
430
- )
431
421
  return None
432
422
 
433
423
 
lamindb_setup/_delete.py CHANGED
@@ -11,6 +11,7 @@ from .core._aws_options import HOSTED_BUCKETS
11
11
  from .core._hub_core import delete_instance as delete_instance_on_hub
12
12
  from .core._hub_core import get_storage_records_for_instance
13
13
  from .core._settings import settings
14
+ from .core._settings_load import load_instance_settings
14
15
  from .core._settings_storage import StorageSettings
15
16
  from .core.upath import LocalPathClasses, check_storage_is_empty
16
17
 
@@ -38,6 +39,8 @@ def delete_exclusion_dir(isettings: InstanceSettings) -> None:
38
39
 
39
40
 
40
41
  def delete_by_isettings(isettings: InstanceSettings) -> None:
42
+ assert isettings.slug != "none/none"
43
+
41
44
  settings_file = isettings._get_settings_file()
42
45
  if settings_file.exists():
43
46
  settings_file.unlink()
@@ -51,12 +54,14 @@ def delete_by_isettings(isettings: InstanceSettings) -> None:
51
54
  "Did not have permission to delete SQLite file:"
52
55
  f" {isettings._sqlite_file}"
53
56
  )
54
- pass
55
57
  # unset the global instance settings
56
- if settings._instance_exists and isettings.slug == settings.instance.slug:
57
- if settings._instance_settings_path.exists():
58
- settings._instance_settings_path.unlink()
59
- settings._instance_settings = None
58
+ isettings_on_disk = load_instance_settings()
59
+ if isettings_on_disk.slug == isettings.slug:
60
+ settings._instance_settings_path.unlink() # current instance settings file
61
+ # settings.instance can differ from instance in current_settings_file()
62
+ # due to connect() in the same process
63
+ if settings.instance.slug == isettings.slug:
64
+ settings._instance_settings = None
60
65
 
61
66
 
62
67
  def delete(slug: str, force: bool = False, require_empty: bool = True) -> int | None:
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from lamin_utils import logger
4
4
 
5
5
  from .core._settings import settings
6
- from .core._settings_store import current_instance_settings_file
6
+ from .core._settings_load import load_instance_settings
7
7
  from .core.cloud_sqlite_locker import clear_locker
8
8
 
9
9
 
@@ -15,10 +15,11 @@ def disconnect(mute: bool = False) -> None:
15
15
  See Also:
16
16
  Clear default instance configuration via the CLI, see `here <https://docs.lamin.ai/cli#disconnect>`__.
17
17
  """
18
- if current_instance_settings_file().exists():
19
- instance = settings.instance.slug
18
+ # settings._instance_exists can be true due to connect even without having a file
19
+ if settings._instance_exists:
20
+ instance = settings.instance
20
21
  try:
21
- settings.instance._update_cloud_sqlite_file()
22
+ instance._update_cloud_sqlite_file()
22
23
  except Exception as e:
23
24
  if isinstance(e, FileNotFoundError):
24
25
  logger.warning("did not find local cache file")
@@ -26,10 +27,12 @@ def disconnect(mute: bool = False) -> None:
26
27
  logger.warning("did not upload cache file - not enough permissions")
27
28
  else:
28
29
  raise e
29
- current_instance_settings_file().unlink()
30
30
  clear_locker()
31
+ # instance in current instance file can differ from instance in settings
32
+ if load_instance_settings().slug == instance.slug:
33
+ settings._instance_settings_path.unlink(missing_ok=True)
34
+ settings._instance_settings = None
31
35
  if not mute:
32
- logger.success(f"disconnected instance: {instance}")
33
- else:
34
- if not mute:
35
- logger.info("no instance loaded")
36
+ logger.success(f"disconnected instance: {instance.slug}")
37
+ elif not mute:
38
+ logger.info("no instance loaded")
@@ -340,7 +340,6 @@ def init(
340
340
  from ._schema_metadata import update_schema_in_hub
341
341
 
342
342
  update_schema_in_hub(access_token=access_token)
343
- importlib.reload(importlib.import_module("lamindb"))
344
343
  reset_django_module_variables()
345
344
  logger.important(f"initialized lamindb: {isettings.slug}")
346
345
  except Exception as e:
lamindb_setup/_migrate.py CHANGED
@@ -1,5 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import os
4
+
3
5
  import httpx
4
6
  from django.db import connection
5
7
  from django.db.migrations.loader import MigrationLoader
@@ -98,16 +100,19 @@ class migrate:
98
100
 
99
101
  @classmethod
100
102
  def deploy(cls, package_name: str | None = None, number: int | None = None) -> None:
101
- import os
103
+ assert settings._instance_exists, (
104
+ "Not connected to an instance, please connect to migrate."
105
+ )
102
106
 
103
107
  # NOTE: this is a temporary solution to avoid breaking tests
104
108
  LAMIN_MIGRATE_ON_LAMBDA = (
105
- os.environ.get("LAMIN_MIGRATE_ON_LAMBDA", "false") == "true"
109
+ os.getenv("LAMIN_MIGRATE_ON_LAMBDA", "false") == "true"
106
110
  )
111
+ isettings = settings.instance
107
112
 
108
- if settings.instance.is_on_hub and LAMIN_MIGRATE_ON_LAMBDA:
113
+ if isettings.is_on_hub and LAMIN_MIGRATE_ON_LAMBDA:
109
114
  response = httpx.post(
110
- f"{settings.instance.api_url}/instances/{settings.instance._id}/migrate",
115
+ f"{isettings.api_url}/instances/{isettings._id}/migrate",
111
116
  headers={"Authorization": f"Bearer {settings.user.access_token}"},
112
117
  timeout=None, # this can take time
113
118
  )
@@ -125,49 +130,41 @@ class migrate:
125
130
  from lamindb_setup._schema_metadata import update_schema_in_hub
126
131
  from lamindb_setup.core._hub_client import call_with_fallback_auth
127
132
  from lamindb_setup.core._hub_crud import (
128
- select_collaborator,
129
133
  update_instance,
130
134
  )
131
135
 
132
- if settings.instance.is_on_hub:
133
- # double check that user is an admin, otherwise will fail below
134
- # due to insufficient SQL permissions with cryptic error
135
- collaborator = call_with_fallback_auth(
136
- select_collaborator,
137
- instance_id=settings.instance._id,
138
- account_id=settings.user._uuid,
139
- fine_grained_access=settings.instance._fine_grained_access,
140
- )
141
- if collaborator is None or collaborator["role"] != "admin":
142
- raise SystemExit(
143
- "❌ Only admins can deploy migrations, please ensure that you're an"
144
- f" admin: https://lamin.ai/{settings.instance.slug}/settings"
145
- )
136
+ isettings = settings.instance
137
+ is_managed_by_hub = isettings.is_managed_by_hub
138
+ is_on_hub = is_managed_by_hub or isettings.is_on_hub
139
+
140
+ if is_managed_by_hub and "root" not in isettings.db:
146
141
  # ensure we connect with the root user
147
- if "root" not in settings.instance.db:
148
- connect(use_root_db_user=True)
149
- assert "root" in (instance_db := settings.instance.db), instance_db
142
+ connect(use_root_db_user=True)
143
+ assert "root" in (instance_db := settings.instance.db), instance_db
144
+ if is_on_hub:
150
145
  # we need lamindb to be installed, otherwise we can't populate the version
151
146
  # information in the hub
147
+ # this also connects
152
148
  import lamindb
153
-
149
+ # this is needed to avoid connecting on importing apps inside setup_django process
150
+ setup_django_disable_autoconnect = disable_auto_connect(setup_django)
154
151
  # this sets up django and deploys the migrations
155
152
  if package_name is not None and number is not None:
156
- setup_django(
157
- settings.instance,
153
+ setup_django_disable_autoconnect(
154
+ isettings,
158
155
  deploy_migrations=True,
159
156
  appname_number=(package_name, number),
160
157
  )
161
158
  else:
162
- setup_django(settings.instance, deploy_migrations=True)
159
+ setup_django_disable_autoconnect(isettings, deploy_migrations=True)
163
160
  # this populates the hub
164
- if settings.instance.is_on_hub:
161
+ if is_on_hub:
165
162
  logger.important(f"updating lamindb version in hub: {lamindb.__version__}")
166
- if settings.instance.dialect != "sqlite":
163
+ if isettings.dialect != "sqlite":
167
164
  update_schema_in_hub()
168
165
  call_with_fallback_auth(
169
166
  update_instance,
170
- instance_id=settings.instance._id.hex,
167
+ instance_id=isettings._id.hex,
171
168
  instance_fields={"lamindb_version": lamindb.__version__},
172
169
  )
173
170
 
@@ -175,16 +172,45 @@ class migrate:
175
172
  @disable_auto_connect
176
173
  def check(cls) -> bool:
177
174
  """Check whether Registry definitions are in sync with migrations."""
175
+ import io
176
+
178
177
  from django.core.management import call_command
179
178
 
180
179
  setup_django(settings.instance)
180
+
181
+ # Capture stdout/stderr to show what migrations are needed if check fails
182
+ stdout = io.StringIO()
183
+ stderr = io.StringIO()
184
+
181
185
  try:
182
- call_command("makemigrations", check_changes=True)
186
+ call_command(
187
+ "makemigrations", check_changes=True, stdout=stdout, stderr=stderr
188
+ )
183
189
  except SystemExit:
184
190
  logger.error(
185
191
  "migrations are not in sync with ORMs, please create a migration: lamin"
186
192
  " migrate create"
187
193
  )
194
+ # Print captured output from the check
195
+ if stdout.getvalue():
196
+ logger.error(f"makemigrations --check stdout:\n{stdout.getvalue()}")
197
+ if stderr.getvalue():
198
+ logger.error(f"makemigrations --check stderr:\n{stderr.getvalue()}")
199
+
200
+ # Run makemigrations --dry-run to show what would be created
201
+ stdout2 = io.StringIO()
202
+ stderr2 = io.StringIO()
203
+ try:
204
+ call_command(
205
+ "makemigrations", dry_run=True, stdout=stdout2, stderr=stderr2
206
+ )
207
+ except SystemExit:
208
+ pass
209
+ if stdout2.getvalue():
210
+ logger.error(f"makemigrations --dry-run stdout:\n{stdout2.getvalue()}")
211
+ if stderr2.getvalue():
212
+ logger.error(f"makemigrations --dry-run stderr:\n{stderr2.getvalue()}")
213
+
188
214
  return False
189
215
  return True
190
216
 
@@ -16,6 +16,9 @@ if TYPE_CHECKING:
16
16
  def set_managed_storage(root: UPathStr, host: str | None = None, **fs_kwargs):
17
17
  """Add or switch to another managed storage location.
18
18
 
19
+ Note: This function should be called `set_writeable_storage_location` instead. But likely it will disappear
20
+ in refactoring that consolidates with the `ln.Storage()` path.
21
+
19
22
  Args:
20
23
  root: `UPathStr` - The new storage root, e.g., an S3 bucket.
21
24
  host: `str | None = None` For a shared local storage location, pass a globally unique host identifier, e.g. `"my-institute-cluster-1"`, `"my-server-abcd"`, ...
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ from time import sleep
4
5
  from typing import TYPE_CHECKING
5
6
 
6
7
  from lamin_utils import logger
@@ -59,15 +60,14 @@ def login(
59
60
 
60
61
  `login()` prompts for your API key unless you set it via the `LAMIN_API_KEY` environment variable or pass it as an argument.
61
62
 
62
- You can create your API key in your account settings on LaminHub (top right corner).
63
+ You can create your API key in your account settings on LaminHub at `lamin.ai/settings <https://lamin.ai/settings>`__.
64
+
65
+ Note that the preferred method is to use the CLI command `lamin login`: `docs.lamin.ai/cli#login <https://docs.lamin.ai/cli#login>`__.
63
66
 
64
67
  Args:
65
68
  user: User handle.
66
69
  api_key: API key.
67
70
 
68
- See Also:
69
- Login via the CLI command `lamin login`, see `here <https://docs.lamin.ai/cli#login>`__.
70
-
71
71
  Examples:
72
72
 
73
73
  Logging in the first time::
@@ -80,12 +80,15 @@ def login(
80
80
 
81
81
  ln.setup.login("myhandle") # pass your user handle
82
82
  """
83
+ from getpass import getpass
84
+
83
85
  if user is None:
84
86
  if api_key is None:
85
87
  if "LAMIN_API_KEY" in os.environ:
86
88
  api_key = os.environ["LAMIN_API_KEY"]
87
89
  else:
88
- api_key = input("Your API key: ")
90
+ print("Copy your API key. To create one: https://lamin.ai/settings")
91
+ api_key = getpass("Paste it: ")
89
92
  elif api_key is not None:
90
93
  raise ValueError("Please provide either 'user' or 'api_key', not both.")
91
94
 
@@ -25,6 +25,8 @@ def silence_loggers():
25
25
  set_stream_logger(name="botocore.auth", level=logging.WARNING)
26
26
  set_stream_logger(name="botocore.endpoint", level=logging.WARNING)
27
27
  set_stream_logger(name="httpx", level=logging.WARNING)
28
+ set_stream_logger(name="httpcore", level=logging.WARNING)
29
+ set_stream_logger(name="hpack", level=logging.WARNING)
28
30
  try:
29
31
  import aiobotocore
30
32
 
@@ -24,9 +24,6 @@ Storage
24
24
 
25
25
  from . import django, upath
26
26
  from ._clone import (
27
- connect_local_sqlite,
28
- connect_remote_sqlite,
29
- init_local_sqlite,
30
27
  upload_sqlite_clone,
31
28
  )
32
29
  from ._deprecated import deprecated # documented in lamindb.base