lamindb_setup 0.78.0__tar.gz → 0.80.0__tar.gz

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 (100) hide show
  1. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/PKG-INFO +1 -1
  2. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-cloud/02-connect-local-instance.ipynb +0 -23
  3. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-cloud/08-test-multi-session.ipynb +7 -3
  4. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-prod/test-cloud-sync.ipynb +13 -13
  5. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/__init__.py +1 -1
  6. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_connect_instance.py +8 -2
  7. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_init_instance.py +21 -9
  8. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_setup_user.py +3 -3
  9. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_hub_client.py +9 -4
  10. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_settings.py +41 -0
  11. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_settings_storage.py +12 -16
  12. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_settings_user.py +2 -2
  13. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/django.py +11 -1
  14. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/upath.py +2 -6
  15. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/test_edge_request.py +1 -1
  16. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/test_init_instance.py +6 -0
  17. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-local/test_all.py +6 -0
  18. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/.github/workflows/build.yml +0 -0
  19. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/.github/workflows/doc-changes.yml +0 -0
  20. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/.gitignore +0 -0
  21. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/.pre-commit-config.yaml +0 -0
  22. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/LICENSE +0 -0
  23. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/README.md +0 -0
  24. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/changelog.md +0 -0
  25. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-cloud/01-init-local-instance.ipynb +0 -0
  26. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-cloud/03-add-managed-storage.ipynb +0 -0
  27. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-cloud/04-test-bionty.ipynb +0 -0
  28. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-cloud/05-init-hosted-instance.ipynb +0 -0
  29. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-cloud/06-connect-hosted-instance.ipynb +0 -0
  30. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-cloud/07-keep-artifacts-local.ipynb +0 -0
  31. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-cloud/test_notebooks.py +0 -0
  32. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-prod/test-cache-management.ipynb +0 -0
  33. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-prod/test-connect-anonymously.ipynb +0 -0
  34. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-prod/test-empty-init.ipynb +0 -0
  35. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-prod/test-import-schema.ipynb +0 -0
  36. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-prod/test-init-load-local-anonymously.ipynb +0 -0
  37. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-prod/test-insufficient-user-info.ipynb +0 -0
  38. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-prod/test-invalid-schema.ipynb +0 -0
  39. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-prod/test-sqlite-lock.ipynb +0 -0
  40. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/hub-prod/test_notebooks2.py +0 -0
  41. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/index.md +0 -0
  42. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/notebooks.md +0 -0
  43. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/docs/reference.md +0 -0
  44. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_cache.py +0 -0
  45. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_check.py +0 -0
  46. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_check_setup.py +0 -0
  47. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_close.py +0 -0
  48. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_delete.py +0 -0
  49. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_django.py +0 -0
  50. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_entry_points.py +0 -0
  51. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_exportdb.py +0 -0
  52. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_importdb.py +0 -0
  53. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_migrate.py +0 -0
  54. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_register_instance.py +0 -0
  55. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_schema.py +0 -0
  56. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_schema_metadata.py +0 -0
  57. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_set_managed_storage.py +0 -0
  58. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/_silence_loggers.py +0 -0
  59. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/__init__.py +0 -0
  60. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_aws_credentials.py +0 -0
  61. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_aws_storage.py +0 -0
  62. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_deprecated.py +0 -0
  63. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_docs.py +0 -0
  64. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_hub_core.py +0 -0
  65. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_hub_crud.py +0 -0
  66. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_hub_utils.py +0 -0
  67. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_private_django_api.py +0 -0
  68. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_settings_instance.py +0 -0
  69. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_settings_load.py +0 -0
  70. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_settings_save.py +0 -0
  71. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_settings_store.py +0 -0
  72. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/_setup_bionty_sources.py +0 -0
  73. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/cloud_sqlite_locker.py +0 -0
  74. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/exceptions.py +0 -0
  75. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/hashing.py +0 -0
  76. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/lamindb_setup/core/types.py +0 -0
  77. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/noxfile.py +0 -0
  78. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/pyproject.toml +0 -0
  79. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/scripts/script-init-pass-user-no-writes.py +0 -0
  80. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/scripts/script-to-fail-managed-storage.py +0 -0
  81. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/test_connect_instance.py +0 -0
  82. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/test_delete_instance.py +0 -0
  83. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/test_fail_managed_storage.py +0 -0
  84. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/test_init_pass_user_no_writes.py +0 -0
  85. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/test_login.py +0 -0
  86. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/test_migrate.py +0 -0
  87. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-cloud/test_set_storage.py +0 -0
  88. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-local/conftest.py +0 -0
  89. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-local/test_update_schema_in_hub.py +0 -0
  90. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-prod/conftest.py +0 -0
  91. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-prod/test_django.py +0 -0
  92. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-prod/test_global_settings.py +0 -0
  93. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-prod/test_switch_and_fallback_env.py +0 -0
  94. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/hub-prod/test_upath.py +0 -0
  95. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/storage/test_entry_point.py +0 -0
  96. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/storage/test_hashing.py +0 -0
  97. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/storage/test_storage_access.py +0 -0
  98. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/storage/test_storage_basis.py +0 -0
  99. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/storage/test_storage_stats.py +0 -0
  100. {lamindb_setup-0.78.0 → lamindb_setup-0.80.0}/tests/storage/test_to_url.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lamindb_setup
3
- Version: 0.78.0
3
+ Version: 0.80.0
4
4
  Summary: Setup & configure LaminDB.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Requires-Python: >=3.9
@@ -85,29 +85,6 @@
85
85
  ")"
86
86
  ]
87
87
  },
88
- {
89
- "cell_type": "markdown",
90
- "id": "0986a5f3",
91
- "metadata": {},
92
- "source": [
93
- "You cannot load another instance in the same Python session:"
94
- ]
95
- },
96
- {
97
- "cell_type": "code",
98
- "execution_count": null,
99
- "id": "bc3568ae",
100
- "metadata": {},
101
- "outputs": [],
102
- "source": [
103
- "import pytest\n",
104
- "\n",
105
- "with pytest.raises(RuntimeError):\n",
106
- " ln_setup.connect(\"testuser2/mydata\")\n",
107
- "\n",
108
- "assert ln_setup.settings.instance.slug == \"testuser1/mydata\""
109
- ]
110
- },
111
88
  {
112
89
  "cell_type": "markdown",
113
90
  "id": "e3701120",
@@ -113,14 +113,18 @@
113
113
  "metadata": {},
114
114
  "outputs": [],
115
115
  "source": [
116
- "with pytest.raises(RuntimeError):\n",
116
+ "from lamindb_setup._init_instance import CannotSwitchDefaultInstance\n",
117
+ "\n",
118
+ "with pytest.raises(CannotSwitchDefaultInstance):\n",
117
119
  " ln_setup.init(storage=\"./testsetup2\")\n",
118
- "with pytest.raises(RuntimeError):\n",
120
+ "with pytest.raises(CannotSwitchDefaultInstance):\n",
119
121
  " ln_setup.connect(\"testsetup\")\n",
120
122
  "with pytest.raises(RuntimeError):\n",
121
123
  " ln_setup.migrate.create()\n",
122
124
  "with pytest.raises(RuntimeError):\n",
123
- " ln_setup.migrate.deploy()"
125
+ " ln_setup.migrate.deploy()\n",
126
+ "\n",
127
+ "assert ln_setup.settings.instance.slug == \"testuser1/testsetup\""
124
128
  ]
125
129
  },
126
130
  {
@@ -82,7 +82,7 @@
82
82
  "metadata": {},
83
83
  "outputs": [],
84
84
  "source": [
85
- "dir_sync_local = settings.storage.cloud_to_local_no_update(dir_sync)\n",
85
+ "dir_sync_local = settings.paths.cloud_to_local_no_update(dir_sync)\n",
86
86
  "if dir_sync_local.is_dir():\n",
87
87
  " for file in dir_sync_local.iterdir():\n",
88
88
  " file.unlink()\n",
@@ -118,9 +118,9 @@
118
118
  "outputs": [],
119
119
  "source": [
120
120
  "test_local_path = UPath(\"./some/local/path\")\n",
121
- "assert settings.storage.cloud_to_local_no_update(test_local_path) == test_local_path\n",
122
- "assert settings.storage.cloud_to_local_no_update(test_local_path.as_posix()) == test_local_path\n",
123
- "assert settings.storage.cloud_to_local_no_update(test_local_path, cache_key=\"some/cache/key\") == test_local_path"
121
+ "assert settings.paths.cloud_to_local_no_update(test_local_path) == test_local_path\n",
122
+ "assert settings.paths.cloud_to_local_no_update(test_local_path.as_posix()) == test_local_path\n",
123
+ "assert settings.paths.cloud_to_local_no_update(test_local_path, cache_key=\"some/cache/key\") == test_local_path"
124
124
  ]
125
125
  },
126
126
  {
@@ -130,8 +130,8 @@
130
130
  "metadata": {},
131
131
  "outputs": [],
132
132
  "source": [
133
- "assert settings.storage.cloud_to_local_no_update(dir_sync) == settings.cache_dir / f\"lamindb-ci/{instance_name}/dir_sync\"\n",
134
- "assert settings.storage.cloud_to_local_no_update(dir_sync, cache_key=\"dir_cache/key\") == settings.cache_dir / \"dir_cache/key\""
133
+ "assert settings.paths.cloud_to_local_no_update(dir_sync) == settings.cache_dir / f\"lamindb-ci/{instance_name}/dir_sync\"\n",
134
+ "assert settings.paths.cloud_to_local_no_update(dir_sync, cache_key=\"dir_cache/key\") == settings.cache_dir / \"dir_cache/key\""
135
135
  ]
136
136
  },
137
137
  {
@@ -149,7 +149,7 @@
149
149
  "metadata": {},
150
150
  "outputs": [],
151
151
  "source": [
152
- "dir_sync_local = settings.storage.cloud_to_local(dir_sync, cache_key=\"dir_cache/key\")"
152
+ "dir_sync_local = settings.paths.cloud_to_local(dir_sync, cache_key=\"dir_cache/key\")"
153
153
  ]
154
154
  },
155
155
  {
@@ -191,7 +191,7 @@
191
191
  "metadata": {},
192
192
  "outputs": [],
193
193
  "source": [
194
- "dir_sync_local = settings.storage.cloud_to_local(dir_sync)"
194
+ "dir_sync_local = settings.paths.cloud_to_local(dir_sync)"
195
195
  ]
196
196
  },
197
197
  {
@@ -238,7 +238,7 @@
238
238
  "metadata": {},
239
239
  "outputs": [],
240
240
  "source": [
241
- "dir_sync_local = settings.storage.cloud_to_local(dir_sync)\n",
241
+ "dir_sync_local = settings.paths.cloud_to_local(dir_sync)\n",
242
242
  "assert local_file.exists()\n",
243
243
  "assert num_files(dir_sync_local) == 2"
244
244
  ]
@@ -267,7 +267,7 @@
267
267
  "metadata": {},
268
268
  "outputs": [],
269
269
  "source": [
270
- "dir_sync_local = settings.storage.cloud_to_local(dir_sync)\n",
270
+ "dir_sync_local = settings.paths.cloud_to_local(dir_sync)\n",
271
271
  "\n",
272
272
  "for file in (\"file1\", \"file2\"):\n",
273
273
  " assert (dir_sync_local / file).stat().st_mtime == (\n",
@@ -298,7 +298,7 @@
298
298
  "metadata": {},
299
299
  "outputs": [],
300
300
  "source": [
301
- "dir_sync_local = settings.storage.cloud_to_local(dir_sync)\n",
301
+ "dir_sync_local = settings.paths.cloud_to_local(dir_sync)\n",
302
302
  "assert num_files(dir_sync_local) == 2\n",
303
303
  "assert local_file_new.exists()"
304
304
  ]
@@ -325,7 +325,7 @@
325
325
  "metadata": {},
326
326
  "outputs": [],
327
327
  "source": [
328
- "dir_sync_local = settings.storage.cloud_to_local(dir_sync)\n",
328
+ "dir_sync_local = settings.paths.cloud_to_local(dir_sync)\n",
329
329
  "\n",
330
330
  "assert num_files(dir_sync_local) == 2\n",
331
331
  "assert not local_file_new.exists()\n",
@@ -430,7 +430,7 @@
430
430
  "metadata": {},
431
431
  "outputs": [],
432
432
  "source": [
433
- "cache_file = settings.instance.storage.cloud_to_local_no_update(sqlite_file)"
433
+ "cache_file = settings.paths.cloud_to_local_no_update(sqlite_file)"
434
434
  ]
435
435
  },
436
436
  {
@@ -33,7 +33,7 @@ Modules & settings:
33
33
 
34
34
  """
35
35
 
36
- __version__ = "0.78.0" # denote a release candidate for 0.1.0 with 0.1rc1
36
+ __version__ = "0.80.0" # denote a release candidate for 0.1.0 with 0.1rc1
37
37
 
38
38
  import os as _os
39
39
  import sys as _sys
@@ -9,7 +9,11 @@ from lamin_utils import logger
9
9
 
10
10
  from ._check_setup import _check_instance_setup
11
11
  from ._close import close as close_instance
12
- from ._init_instance import MESSAGE_NO_MULTIPLE_INSTANCE, load_from_isettings
12
+ from ._init_instance import (
13
+ MESSAGE_CANNOT_SWITCH_DEFAULT_INSTANCE,
14
+ CannotSwitchDefaultInstance,
15
+ load_from_isettings,
16
+ )
13
17
  from ._silence_loggers import silence_loggers
14
18
  from .core._hub_core import connect_instance_hub
15
19
  from .core._hub_utils import (
@@ -224,7 +228,9 @@ def connect(slug: str, **kwargs) -> str | tuple | None:
224
228
  logger.info(f"connected lamindb: {settings.instance.slug}")
225
229
  return None
226
230
  else:
227
- raise RuntimeError(MESSAGE_NO_MULTIPLE_INSTANCE)
231
+ raise CannotSwitchDefaultInstance(
232
+ MESSAGE_CANNOT_SWITCH_DEFAULT_INSTANCE
233
+ )
228
234
  elif (
229
235
  _write_settings
230
236
  and settings._instance_exists
@@ -26,7 +26,7 @@ if TYPE_CHECKING:
26
26
  from .core.types import UPathStr
27
27
 
28
28
 
29
- def get_schema_module_name(schema_name) -> str:
29
+ def get_schema_module_name(schema_name, raise_import_error: bool = True) -> str | None:
30
30
  import importlib.util
31
31
 
32
32
  name_attempts = [f"lnschema_{schema_name.replace('-', '_')}", schema_name]
@@ -34,9 +34,11 @@ def get_schema_module_name(schema_name) -> str:
34
34
  module_spec = importlib.util.find_spec(name)
35
35
  if module_spec is not None:
36
36
  return name
37
- raise ImportError(
38
- f"Python package for '{schema_name}' is not installed.\nIf your package is on PyPI, run `pip install {schema_name}`"
39
- )
37
+ message = f"Schema module '{schema_name}' is not installed → no access to its labels & registries (resolve via `pip install {schema_name}`)"
38
+ if raise_import_error:
39
+ raise ImportError(message)
40
+ logger.warning(message.lower())
41
+ return None
40
42
 
41
43
 
42
44
  def register_storage_in_instance(ssettings: StorageSettings):
@@ -180,6 +182,8 @@ def validate_init_args(
180
182
  validate_schema_arg,
181
183
  )
182
184
 
185
+ if storage is None:
186
+ raise SystemExit("✗ `storage` argument can't be `None`")
183
187
  # should be called as the first thing
184
188
  name_str = infer_instance_name(storage=storage, name=name, db=db)
185
189
  owner_str = settings.user.handle if _user is None else _user.handle
@@ -206,10 +210,18 @@ def validate_init_args(
206
210
  return name_str, instance_id, instance_state, instance_slug
207
211
 
208
212
 
209
- MESSAGE_NO_MULTIPLE_INSTANCE = """
210
- Currently don't support subsequent connection to different databases in the same
211
- Python session.\n
212
- Try running on the CLI: lamin settings set auto-connect false
213
+ class CannotSwitchDefaultInstance(SystemExit):
214
+ pass
215
+
216
+
217
+ MESSAGE_CANNOT_SWITCH_DEFAULT_INSTANCE = """
218
+ You cannot write to different instances in the same Python session.
219
+
220
+ Do you want to read from another instance via `Record.using()`? For example:
221
+
222
+ ln.Artifact.using("laminlabs/cellxgene").filter()
223
+
224
+ Or do you want to switch off auto-connect via `lamin settings set auto-connect false`?
213
225
  """
214
226
 
215
227
 
@@ -248,7 +260,7 @@ def init(
248
260
  from ._check_setup import _check_instance_setup
249
261
 
250
262
  if _check_instance_setup() and not _test:
251
- raise RuntimeError(MESSAGE_NO_MULTIPLE_INSTANCE)
263
+ raise CannotSwitchDefaultInstance(MESSAGE_CANNOT_SWITCH_DEFAULT_INSTANCE)
252
264
  elif _write_settings:
253
265
  close_instance(mute=True)
254
266
  from .core._hub_core import init_instance as init_instance_hub
@@ -48,14 +48,14 @@ def load_user(email: str | None = None, handle: str | None = None) -> UserSettin
48
48
 
49
49
 
50
50
  def login(
51
- user: str | None = None, *, key: str | None = None, api_key: str | None = None
51
+ user: str | None = None, *, api_key: str | None = None, key: str | None = None
52
52
  ) -> None:
53
53
  """Log in user.
54
54
 
55
55
  Args:
56
56
  user: handle or email
57
- key: API key
58
- api_key: Beta API key
57
+ api_key: API key
58
+ key: legacy API key
59
59
  """
60
60
  if user is None and api_key is None:
61
61
  if "LAMIN_API_KEY" in os.environ:
@@ -10,6 +10,8 @@ from pydantic_settings import BaseSettings
10
10
  from supabase import Client, create_client # type: ignore
11
11
  from supabase.lib.client_options import ClientOptions
12
12
 
13
+ from ._settings_save import save_user_settings
14
+
13
15
 
14
16
  class Connector(BaseSettings):
15
17
  url: str
@@ -129,14 +131,17 @@ def call_with_fallback_auth(
129
131
 
130
132
  for renew_token, fallback_env in [(False, False), (True, False), (False, True)]:
131
133
  try:
132
- if renew_token:
133
- logger.warning(
134
- "renewing expired lamin token: call `lamin login <your-handle>` to avoid this"
135
- )
136
134
  client = connect_hub_with_auth(
137
135
  renew_token=renew_token, fallback_env=fallback_env
138
136
  )
139
137
  result = callable(**kwargs, client=client)
138
+ # we update access_token here
139
+ # because at this point the call has been successfully resolved
140
+ if renew_token:
141
+ from lamindb_setup import settings
142
+
143
+ # here settings.user contains an updated access_token
144
+ save_user_settings(settings.user)
140
145
  break
141
146
  # we use Exception here as the ways in which the client fails upon 401
142
147
  # are not consistent and keep changing
@@ -161,6 +161,15 @@ class SetupSettings:
161
161
  cache_dir.mkdir(parents=True, exist_ok=True)
162
162
  return cache_dir
163
163
 
164
+ @property
165
+ def paths(self) -> type[SetupPaths]:
166
+ """Convert cloud paths to lamidb local paths.
167
+
168
+ Use `settings.paths.cloud_to_local_no_update`
169
+ or `settings.paths.cloud_to_local`.
170
+ """
171
+ return SetupPaths
172
+
164
173
  def __repr__(self) -> str:
165
174
  """Rich string representation."""
166
175
  repr = self.user.__repr__()
@@ -174,6 +183,38 @@ class SetupSettings:
174
183
  return repr
175
184
 
176
185
 
186
+ class SetupPaths:
187
+ """A static class for conversion of cloud paths to lamindb local paths."""
188
+
189
+ @staticmethod
190
+ def cloud_to_local_no_update(
191
+ filepath: UPathStr, cache_key: str | None = None
192
+ ) -> UPath:
193
+ """Local (or local cache) filepath from filepath without synchronization."""
194
+ # cache_key is ignored if filepath is a string or a local path
195
+ # ignores a mere string even if it represents a cloud path
196
+ if isinstance(filepath, UPath) and not isinstance(filepath, LocalPathClasses):
197
+ # settings is defined further in this file
198
+ local_filepath = settings.cache_dir / (
199
+ filepath.path if cache_key is None else cache_key
200
+ )
201
+ else:
202
+ local_filepath = filepath
203
+ return UPath(local_filepath)
204
+
205
+ @staticmethod
206
+ def cloud_to_local(
207
+ filepath: UPathStr, cache_key: str | None = None, **kwargs
208
+ ) -> UPath:
209
+ """Local (or local cache) filepath from filepath."""
210
+ # cache_key is ignored in cloud_to_local_no_update if filepath is local or a string
211
+ local_filepath = SetupPaths.cloud_to_local_no_update(filepath, cache_key)
212
+ if isinstance(filepath, UPath) and not isinstance(filepath, LocalPathClasses):
213
+ local_filepath.parent.mkdir(parents=True, exist_ok=True)
214
+ filepath.synchronize(local_filepath, **kwargs)
215
+ return local_filepath
216
+
217
+
177
218
  def get_env_name():
178
219
  if "LAMIN_ENV" in os.environ:
179
220
  return os.environ["LAMIN_ENV"]
@@ -85,8 +85,8 @@ def init_storage(
85
85
  StorageSettings,
86
86
  Literal["hub-record-not-created", "hub-record-retireved", "hub-record-created"],
87
87
  ]:
88
- if root is None:
89
- raise ValueError("`storage` argument can't be `None`")
88
+ assert root is not None, "`root` argument can't be `None`"
89
+
90
90
  root_str = str(root) # ensure we have a string
91
91
  if ".lamindb" in root_str:
92
92
  raise ValueError(
@@ -322,24 +322,20 @@ class StorageSettings:
322
322
  self, filepath: UPathStr, cache_key: str | None = None, **kwargs
323
323
  ) -> UPath:
324
324
  """Local (or local cache) filepath from filepath."""
325
- # cache_key is ignored in cloud_to_local_no_update if filepath is local
326
- local_filepath = self.cloud_to_local_no_update(filepath, cache_key)
327
- if isinstance(filepath, UPath) and not isinstance(filepath, LocalPathClasses):
328
- local_filepath.parent.mkdir(parents=True, exist_ok=True)
329
- filepath.synchronize(local_filepath, **kwargs)
330
- return local_filepath
325
+ from lamindb_setup import settings
326
+
327
+ return settings.paths.cloud_to_local(
328
+ filepath=filepath, cache_key=cache_key, **kwargs
329
+ )
331
330
 
332
331
  def cloud_to_local_no_update(
333
332
  self, filepath: UPathStr, cache_key: str | None = None
334
333
  ) -> UPath:
335
- # cache_key is ignored if filepath is local
336
- if isinstance(filepath, UPath) and not isinstance(filepath, LocalPathClasses):
337
- local_filepath = self.cache_dir / (
338
- filepath.path if cache_key is None else cache_key
339
- )
340
- else:
341
- local_filepath = filepath
342
- return UPath(local_filepath)
334
+ from lamindb_setup import settings
335
+
336
+ return settings.paths.cloud_to_local_no_update(
337
+ filepath=filepath, cache_key=cache_key
338
+ )
343
339
 
344
340
  def key_to_filepath(self, filekey: UPathStr) -> UPath:
345
341
  """Cloud or local filepath from filekey."""
@@ -24,9 +24,9 @@ class UserSettings:
24
24
  email: str | None = None
25
25
  """User email."""
26
26
  api_key: str | None = None
27
- """Beta API key."""
27
+ """API key."""
28
28
  password: str | None = None
29
- """API key or legacy password."""
29
+ """legacy API key or legacy password."""
30
30
  access_token: str | None = None
31
31
  """User access token."""
32
32
  uid: str = "null"
@@ -55,7 +55,17 @@ def setup_django(
55
55
  from .._init_instance import get_schema_module_name
56
56
 
57
57
  schema_names = ["core"] + list(isettings.schema)
58
- installed_apps = [get_schema_module_name(n) for n in schema_names]
58
+ raise_import_error = True if init else False
59
+ installed_apps = [
60
+ package_name
61
+ for n in schema_names
62
+ if (
63
+ package_name := get_schema_module_name(
64
+ n, raise_import_error=raise_import_error
65
+ )
66
+ )
67
+ is not None
68
+ ]
59
69
  if view_schema:
60
70
  installed_apps = installed_apps[::-1] # to fix how apps appear
61
71
  installed_apps += ["schema_graph", "django.contrib.staticfiles"]
@@ -763,13 +763,9 @@ def check_storage_is_empty(
763
763
  if raise_error
764
764
  else "consider deleting them"
765
765
  )
766
- hint = "'_is_initialized'"
767
- if n_offset_objects == 2:
768
- hint += " & SQLite file"
769
- hint += " ignored"
770
766
  message = (
771
- f"Storage {directory_string} contains {n_objects - n_offset_objects} objects "
772
- f"({hint}) - {ask_for_deletion}"
767
+ f"Storage '{directory_string}' contains {n_objects - n_offset_objects} objects"
768
+ f" - {ask_for_deletion}"
773
769
  )
774
770
  if n_diff > 0:
775
771
  if raise_error:
@@ -43,7 +43,7 @@ def test_edge_request():
43
43
  assert instance["owner"] == "laminlabs"
44
44
  assert instance["name"] == "lamindata"
45
45
  assert instance["api_url"] == "https://us-east-1.api.lamin.ai"
46
- assert instance["schema_str"] == "bionty,wetlab"
46
+ assert instance["schema_str"].startswith("bionty,wetlab")
47
47
  assert "schema_id" in instance
48
48
  # check that schema_id is well-formed UUID
49
49
  # if not, throws ValueError
@@ -32,6 +32,12 @@ def test_init_instance_postgres_default_name(get_hub_client):
32
32
  ln_setup.delete(instance_name, force=True)
33
33
  except InstanceNotFoundError:
34
34
  pass
35
+ # test init with storage=None
36
+ # this happens when calling CLI lamin init or lamin init --name smth
37
+ with pytest.raises(SystemExit):
38
+ ln_setup.init(storage=None, _test=True)
39
+ with pytest.raises(SystemExit):
40
+ ln_setup.init(storage=None, name="init-to-fail", _test=True)
35
41
  # now, run init
36
42
  ln_setup.init(storage="./mydatapg", db=pgurl, _test=True)
37
43
  assert ln_setup.settings.instance.slug == "testuser2/pgtest"
@@ -342,6 +342,12 @@ def test_connect_instance_hub_corrupted_or_expired_credentials(
342
342
  owner="testadmin1",
343
343
  name=create_myinstance["name"],
344
344
  )
345
+ # check access_token renewal
346
+ access_token = ln_setup.settings.user.access_token
347
+ assert access_token != "corrupted_or_expired_token"
348
+ # check that the access_token was written to the settings
349
+ ln_setup.settings._user_settings = None
350
+ assert ln_setup.settings.user.access_token == access_token
345
351
 
346
352
 
347
353
  def test_init_storage_with_non_existing_bucket(create_testadmin1_session):
File without changes
File without changes