lamindb_setup 0.71.0__tar.gz → 0.71.1__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 (91) hide show
  1. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/PKG-INFO +1 -1
  2. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/changelog.md +4 -0
  3. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-cloud/03-add-managed-storage.ipynb +43 -24
  4. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-cloud/05-init-hosted-instance.ipynb +4 -2
  5. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-cloud/06-connect-hosted-instance.ipynb +2 -0
  6. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/__init__.py +1 -1
  7. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_connect_instance.py +14 -0
  8. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_delete.py +23 -24
  9. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_init_instance.py +6 -2
  10. lamindb_setup-0.71.0/lamindb_setup/_add_remote_storage.py → lamindb_setup-0.71.1/lamindb_setup/_set_managed_storage.py +2 -4
  11. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_hub_core.py +5 -0
  12. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_hub_crud.py +6 -1
  13. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_settings_load.py +1 -0
  14. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_settings_save.py +2 -0
  15. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_settings_storage.py +4 -11
  16. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_settings_store.py +1 -0
  17. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/upath.py +10 -1
  18. lamindb_setup-0.71.1/tests/hub-cloud/test_delete_instance.py +10 -0
  19. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-cloud/test_init_instance.py +5 -1
  20. lamindb_setup-0.71.1/tests/hub-cloud/test_set_storage.py +15 -0
  21. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/storage/test_storage_basis.py +1 -0
  22. lamindb_setup-0.71.0/tests/hub-cloud/test_delete_instance.py +0 -13
  23. lamindb_setup-0.71.0/tests/hub-cloud/test_set_storage.py +0 -11
  24. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/.github/workflows/build.yml +0 -0
  25. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/.github/workflows/latest-changes.jinja2 +0 -0
  26. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/.github/workflows/latest-changes.yml +0 -0
  27. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/.gitignore +0 -0
  28. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/.pre-commit-config.yaml +0 -0
  29. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/LICENSE +0 -0
  30. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/README.md +0 -0
  31. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-cloud/01-init-local-instance.ipynb +0 -0
  32. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-cloud/02-connect-local-instance.ipynb +0 -0
  33. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-cloud/04-test-bionty.ipynb +0 -0
  34. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-cloud/07-keep-artifacts-local.ipynb +0 -0
  35. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-cloud/test-multi-session.ipynb +0 -0
  36. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-cloud/test_notebooks.py +0 -0
  37. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-prod/test-cache-management.ipynb +0 -0
  38. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-prod/test-cloud-sync.ipynb +0 -0
  39. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-prod/test-connect-anonymously.ipynb +0 -0
  40. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-prod/test-empty-init.ipynb +0 -0
  41. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-prod/test-import-schema.ipynb +0 -0
  42. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-prod/test-insufficient-user-info.ipynb +0 -0
  43. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-prod/test-invalid-schema.ipynb +0 -0
  44. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-prod/test-sqlite-lock.ipynb +0 -0
  45. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/hub-prod/test_notebooks2.py +0 -0
  46. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/index.md +0 -0
  47. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/notebooks.md +0 -0
  48. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/docs/reference.md +0 -0
  49. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_cache.py +0 -0
  50. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_check.py +0 -0
  51. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_check_setup.py +0 -0
  52. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_close.py +0 -0
  53. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_django.py +0 -0
  54. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_exportdb.py +0 -0
  55. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_importdb.py +0 -0
  56. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_migrate.py +0 -0
  57. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_register_instance.py +0 -0
  58. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_schema.py +0 -0
  59. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_setup_user.py +0 -0
  60. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/_silence_loggers.py +0 -0
  61. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/__init__.py +0 -0
  62. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_aws_storage.py +0 -0
  63. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_deprecated.py +0 -0
  64. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_docs.py +0 -0
  65. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_hub_client.py +0 -0
  66. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_hub_utils.py +0 -0
  67. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_settings.py +0 -0
  68. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_settings_instance.py +0 -0
  69. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_settings_user.py +0 -0
  70. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/_setup_bionty_sources.py +0 -0
  71. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/cloud_sqlite_locker.py +0 -0
  72. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/django.py +0 -0
  73. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/exceptions.py +0 -0
  74. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/hashing.py +0 -0
  75. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/lamindb_setup/core/types.py +0 -0
  76. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/noxfile.py +0 -0
  77. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/pyproject.toml +0 -0
  78. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-cloud/test_connect_instance.py +0 -0
  79. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-cloud/test_login.py +0 -0
  80. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-cloud/test_migrate.py +0 -0
  81. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-local/conftest.py +0 -0
  82. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-local/test_all.py +0 -0
  83. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-prod/conftest.py +0 -0
  84. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-prod/test_auto_connect.py +0 -0
  85. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-prod/test_django.py +0 -0
  86. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-prod/test_switch_and_fallback_env.py +0 -0
  87. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/hub-prod/test_upath.py +0 -0
  88. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/storage/test_hashing.py +0 -0
  89. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/storage/test_storage_access.py +0 -0
  90. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/tests/storage/test_storage_stats.py +0 -0
  91. {lamindb_setup-0.71.0 → lamindb_setup-0.71.1}/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.71.0
3
+ Version: 0.71.1
4
4
  Summary: Setup & configure LaminDB.
5
5
  Author-email: Lamin Labs <laminlabs@gmail.com>
6
6
  Description-Content-Type: text/markdown
@@ -3,6 +3,10 @@
3
3
  <!-- prettier-ignore -->
4
4
  Name | PR | Developer | Date | Version
5
5
  --- | --- | --- | --- | ---
6
+ ♻️ Extend valid suffixes to composite suffixes | [746](https://github.com/laminlabs/lamindb-setup/pull/746) | [falexwolf](https://github.com/falexwolf) | 2024-05-03 | 0.71.1
7
+ 🐛 Fix test failures | [745](https://github.com/laminlabs/lamindb-setup/pull/745) | [Koncopd](https://github.com/Koncopd) | 2024-05-03 |
8
+ ♻️ Persist keep-artifacts-local in settings.env | [743](https://github.com/laminlabs/lamindb-setup/pull/743) | [falexwolf](https://github.com/falexwolf) | 2024-05-03 |
9
+ ✅ Expand tests to multi users for managed storage | [742](https://github.com/laminlabs/lamindb-setup/pull/742) | [falexwolf](https://github.com/falexwolf) | 2024-05-03 |
6
10
  🐛 Fix delete function | [741](https://github.com/laminlabs/lamindb-setup/pull/741) | [falexwolf](https://github.com/falexwolf) | 2024-04-30 | 0.71.0
7
11
  ✨ Manage multiple storage locations with integrity | [738](https://github.com/laminlabs/lamindb-setup/pull/738) | [falexwolf](https://github.com/falexwolf) | 2024-04-30 |
8
12
  ✨ Proper progress bars for upload and download | [739](https://github.com/laminlabs/lamindb-setup/pull/739) | [Koncopd](https://github.com/Koncopd) | 2024-04-28 |
@@ -38,7 +38,7 @@
38
38
  "import pytest\n",
39
39
  "from pathlib import Path\n",
40
40
  "import lamindb_setup as ln_setup\n",
41
- "from lamindb_setup._add_remote_storage import add_managed_storage"
41
+ "from lamindb_setup._set_managed_storage import set_managed_storage"
42
42
  ]
43
43
  },
44
44
  {
@@ -84,7 +84,7 @@
84
84
  "outputs": [],
85
85
  "source": [
86
86
  "with pytest.raises(ValueError) as error:\n",
87
- " add_managed_storage(\"./storage2\")\n",
87
+ " set_managed_storage(\"./storage2\")\n",
88
88
  "assert error.exconly() == \"ValueError: Can't add additional managed storage locations for instances that aren't managed through the hub.\""
89
89
  ]
90
90
  },
@@ -113,7 +113,7 @@
113
113
  "metadata": {},
114
114
  "outputs": [],
115
115
  "source": [
116
- "add_managed_storage(\"./storage2\")"
116
+ "set_managed_storage(\"./storage2\")"
117
117
  ]
118
118
  },
119
119
  {
@@ -148,7 +148,7 @@
148
148
  "metadata": {},
149
149
  "outputs": [],
150
150
  "source": [
151
- "add_managed_storage(\"./storage1\")"
151
+ "set_managed_storage(\"./storage1\")"
152
152
  ]
153
153
  },
154
154
  {
@@ -179,7 +179,7 @@
179
179
  "metadata": {},
180
180
  "outputs": [],
181
181
  "source": [
182
- "add_managed_storage(\"./storage1\")"
182
+ "set_managed_storage(\"./storage1\")"
183
183
  ]
184
184
  },
185
185
  {
@@ -202,7 +202,7 @@
202
202
  "metadata": {},
203
203
  "outputs": [],
204
204
  "source": [
205
- "add_managed_storage(\"./storage2\")"
205
+ "set_managed_storage(\"./storage2\")"
206
206
  ]
207
207
  },
208
208
  {
@@ -234,7 +234,7 @@
234
234
  "metadata": {},
235
235
  "outputs": [],
236
236
  "source": [
237
- "add_managed_storage(\"s3://lamindb-ci/storage3\")"
237
+ "set_managed_storage(\"s3://lamindb-ci/storage3\")"
238
238
  ]
239
239
  },
240
240
  {
@@ -273,7 +273,7 @@
273
273
  "metadata": {},
274
274
  "outputs": [],
275
275
  "source": [
276
- "add_managed_storage(\"s3://lamindb-ci/storage3\", cache_regions=False)"
276
+ "set_managed_storage(\"s3://lamindb-ci/storage3\", cache_regions=False)"
277
277
  ]
278
278
  },
279
279
  {
@@ -302,18 +302,18 @@
302
302
  "metadata": {},
303
303
  "outputs": [],
304
304
  "source": [
305
- "# from laminhub_rest.core.collaborator._add_collaborator import add_collaborator\n",
306
- "# from lamindb_setup.core._hub_client import connect_hub_with_auth\n",
305
+ "from laminhub_rest.core.collaborator._add_collaborator import add_collaborator\n",
306
+ "from lamindb_setup.core._hub_client import connect_hub_with_auth\n",
307
307
  "\n",
308
- "# admin_hub = connect_hub_with_auth()\n",
309
- "# add_collaborator(\n",
310
- "# \"testuser2\",\n",
311
- "# \"testuser1\",\n",
312
- "# \"test-add-managed-storage\",\n",
313
- "# \"write\",\n",
314
- "# admin_hub,\n",
315
- "# )\n",
316
- "# admin_hub.auth.close()"
308
+ "admin_hub = connect_hub_with_auth()\n",
309
+ "add_collaborator(\n",
310
+ " \"testuser2\",\n",
311
+ " \"testuser1\",\n",
312
+ " \"test-add-managed-storage\",\n",
313
+ " \"write\",\n",
314
+ " admin_hub,\n",
315
+ ")\n",
316
+ "admin_hub.auth.close()"
317
317
  ]
318
318
  },
319
319
  {
@@ -321,7 +321,7 @@
321
321
  "id": "e9d64cad",
322
322
  "metadata": {},
323
323
  "source": [
324
- "Another user:"
324
+ "Sign them in and let them add another storage location:"
325
325
  ]
326
326
  },
327
327
  {
@@ -331,9 +331,28 @@
331
331
  "metadata": {},
332
332
  "outputs": [],
333
333
  "source": [
334
- "# ln_setup.login(\"testuser2\")\n",
335
- "# add_managed_storage(\"./storage4\")\n",
336
- "# assert ln_setup.settings.storage.root_as_str == f\"{Path.cwd()}/storage4\""
334
+ "ln_setup.login(\"testuser2\")\n",
335
+ "set_managed_storage(\"./storage4\")\n",
336
+ "assert ln_setup.settings.storage.root_as_str == f\"{Path.cwd()}/storage4\""
337
+ ]
338
+ },
339
+ {
340
+ "cell_type": "markdown",
341
+ "id": "60ac7cb2",
342
+ "metadata": {},
343
+ "source": [
344
+ "Attempt to delete instance with testuser2:"
345
+ ]
346
+ },
347
+ {
348
+ "cell_type": "code",
349
+ "execution_count": null,
350
+ "id": "6de90900",
351
+ "metadata": {},
352
+ "outputs": [],
353
+ "source": [
354
+ "with pytest.raises(PermissionError) as error:\n",
355
+ " ln_setup.delete(\"testuser1/test-add-managed-storage\", force=True)"
337
356
  ]
338
357
  },
339
358
  {
@@ -341,7 +360,7 @@
341
360
  "id": "9d1fd197",
342
361
  "metadata": {},
343
362
  "source": [
344
- "Delete test instance:"
363
+ "Delete test instance through testuser1:"
345
364
  ]
346
365
  },
347
366
  {
@@ -94,7 +94,9 @@
94
94
  "assert ln_setup.settings.instance.owner == ln_setup.settings.user.handle\n",
95
95
  "assert ln_setup.settings.instance.name == \"my-hosted\"\n",
96
96
  "assert ln_setup.settings.storage.root.as_posix().startswith(HOSTED_BUCKETS)\n",
97
- "assert ln_setup.settings.storage.id is not None"
97
+ "assert ln_setup.settings.storage.id is not None\n",
98
+ "\n",
99
+ "assert ln_setup.settings.storage._mark_storage_root.exists()"
98
100
  ]
99
101
  },
100
102
  {
@@ -123,7 +125,7 @@
123
125
  "name": "python",
124
126
  "nbconvert_exporter": "python",
125
127
  "pygments_lexer": "ipython3",
126
- "version": "3.10.13"
128
+ "version": "3.9.17"
127
129
  }
128
130
  },
129
131
  "nbformat": 4,
@@ -145,6 +145,8 @@
145
145
  "target_dir.rmdir()\n",
146
146
  "assert not target_dir.exists()\n",
147
147
  "\n",
148
+ "assert ln_setup.settings.storage._mark_storage_root.exists()\n",
149
+ "\n",
148
150
  "ln_setup.delete(\"my-hosted\", force=True)\n",
149
151
  "delete_instance(f\"testuser1/{instance_name}\")"
150
152
  ]
@@ -34,7 +34,7 @@ Modules & settings:
34
34
 
35
35
  """
36
36
 
37
- __version__ = "0.71.0" # denote a release candidate for 0.1.0 with 0.1rc1
37
+ __version__ = "0.71.1" # denote a release candidate for 0.1.0 with 0.1rc1
38
38
 
39
39
  import sys
40
40
  from os import name as _os_name
@@ -4,6 +4,7 @@ import os
4
4
  from typing import TYPE_CHECKING
5
5
  from uuid import UUID
6
6
 
7
+ from django.db import ProgrammingError
7
8
  from lamin_utils import logger
8
9
 
9
10
  from ._check_setup import _check_instance_setup
@@ -158,6 +159,8 @@ def connect(
158
159
  root=storage_result["root"],
159
160
  region=storage_result["region"],
160
161
  uid=storage_result["lnid"],
162
+ uuid=UUID(storage_result["id"]),
163
+ instance_id=UUID(instance_result["id"]),
161
164
  )
162
165
  isettings = InstanceSettings(
163
166
  id=UUID(instance_result["id"]),
@@ -220,6 +223,17 @@ def connect(
220
223
 
221
224
  if storage is not None and isettings.dialect == "sqlite":
222
225
  update_root_field_in_default_storage(isettings)
226
+ # below is for backfilling the instance_uid value
227
+ # we'll enable it once more people migrated to 0.71.0
228
+ # ssettings_record = isettings.storage.record
229
+ # if ssettings_record.instance_uid is None:
230
+ # ssettings_record.instance_uid = isettings.uid
231
+ # # try saving if not read-only access
232
+ # try:
233
+ # ssettings_record.save()
234
+ # # raised by django when the access is denied
235
+ # except ProgrammingError:
236
+ # pass
223
237
  load_from_isettings(isettings)
224
238
  except Exception as e:
225
239
  if isettings is not None:
@@ -6,7 +6,11 @@ from uuid import UUID
6
6
 
7
7
  from lamin_utils import logger
8
8
 
9
- from ._connect_instance import INSTANCE_NOT_FOUND_MESSAGE
9
+ from ._connect_instance import (
10
+ INSTANCE_NOT_FOUND_MESSAGE,
11
+ InstanceNotFoundError,
12
+ get_owner_name_from_identifier,
13
+ )
10
14
  from .core._hub_core import connect_instance as load_instance_from_hub
11
15
  from .core._hub_core import delete_instance as delete_instance_on_hub
12
16
  from .core._hub_core import get_storage_records_for_instance
@@ -54,55 +58,48 @@ def delete_by_isettings(isettings: InstanceSettings) -> None:
54
58
  settings._instance_settings = None
55
59
 
56
60
 
57
- def delete(
58
- instance_name: str, force: bool = False, require_empty: bool = True
59
- ) -> int | None:
61
+ def delete(slug: str, force: bool = False, require_empty: bool = True) -> int | None:
60
62
  """Delete a LaminDB instance.
61
63
 
62
64
  Args:
63
- instance_name (str): The name of the instance to delete.
64
- force (bool): Whether to skip the confirmation prompt.
65
- require_empty (bool): Whether to check if the instance is empty before deleting.
65
+ slug: The instance slug `account_handle/instance_name` or URL.
66
+ If the instance is owned by you, it suffices to pass the instance name.
67
+ force: Whether to skip the confirmation prompt.
68
+ require_empty: Whether to check if the instance is empty before deleting.
66
69
  """
67
- if "/" in instance_name:
68
- logger.warning(
69
- "Deleting the instance of another user is currently not supported with the"
70
- " CLI. Please provide only the instance name when deleting an instance ('/'"
71
- " delimiter not allowed)."
72
- )
73
- raise ValueError("Invalid instance name: '/' delimiter not allowed.")
74
- instance_slug = f"{settings.user.handle}/{instance_name}"
70
+ instance_owner, instance_name = get_owner_name_from_identifier(slug)
75
71
  if settings._instance_exists and settings.instance.name == instance_name:
76
72
  isettings = settings.instance
77
73
  else:
78
- settings_file = instance_settings_file(instance_name, settings.user.handle)
74
+ settings_file = instance_settings_file(instance_name, instance_owner)
79
75
  if not settings_file.exists():
80
76
  hub_result = load_instance_from_hub(
81
- owner=settings.user.handle, name=instance_name
77
+ owner=instance_owner, name=instance_name
82
78
  )
83
79
  if isinstance(hub_result, str):
84
80
  message = INSTANCE_NOT_FOUND_MESSAGE.format(
85
- owner=settings.user.handle,
81
+ owner=instance_owner,
86
82
  name=instance_name,
87
83
  hub_result=hub_result,
88
84
  )
89
- logger.warning(message)
90
- return None
85
+ raise InstanceNotFoundError(message)
91
86
  instance_result, storage_result = hub_result
92
87
  ssettings = StorageSettings(
93
88
  root=storage_result["root"],
94
89
  region=storage_result["region"],
95
90
  uid=storage_result["lnid"],
91
+ uuid=UUID(storage_result["id"]),
96
92
  )
97
93
  isettings = InstanceSettings(
98
94
  id=UUID(instance_result["id"]),
99
- owner=settings.user.handle,
95
+ owner=instance_owner,
100
96
  name=instance_name,
101
97
  storage=ssettings,
102
98
  keep_artifacts_local=bool(instance_result["keep_artifacts_local"]),
103
99
  db=instance_result["db"] if "db" in instance_result else None,
104
100
  schema=instance_result["schema_str"],
105
101
  git_repo=instance_result["git_repo"],
102
+ is_on_hub=True,
106
103
  )
107
104
  else:
108
105
  isettings = load_instance_settings(settings_file)
@@ -117,7 +114,7 @@ def delete(
117
114
  if not force:
118
115
  valid_responses = ["y", "yes"]
119
116
  user_input = (
120
- input(f"Are you sure you want to delete instance {instance_slug}? (y/n) ")
117
+ input(f"Are you sure you want to delete instance {isettings.slug}? (y/n) ")
121
118
  .strip()
122
119
  .lower()
123
120
  )
@@ -161,10 +158,12 @@ def delete(
161
158
  ssettings._mark_storage_root.unlink(
162
159
  missing_ok=True # this is totally weird, but needed on Py3.11
163
160
  )
164
- logger.info(f"deleting instance {instance_slug}")
161
+ logger.info(f"deleting instance {isettings.slug}")
165
162
  # below we can skip check_storage_is_empty() because we already called
166
163
  # it above
167
- if settings.user.handle != "anonymous":
164
+ if settings.user.handle != "anonymous" and isettings.is_on_hub:
165
+ # start with deleting things on the hub
166
+ # this will error if the user doesn't have permission
168
167
  delete_instance_on_hub(isettings._id, require_empty=False)
169
168
  delete_by_isettings(isettings)
170
169
  # if .lndb file was delete, then we might count -1
@@ -277,10 +277,14 @@ def init(
277
277
 
278
278
  if isettings is not None:
279
279
  delete_by_isettings(isettings)
280
- if settings.user.handle != "anonymous":
280
+ if settings.user.handle != "anonymous" and isettings.is_on_hub:
281
281
  delete_instance_record(isettings._id)
282
282
  isettings._get_settings_file().unlink(missing_ok=True) # type: ignore
283
- if ssettings is not None and settings.user.handle != "anonymous":
283
+ if (
284
+ ssettings is not None
285
+ and settings.user.handle != "anonymous"
286
+ and ssettings.is_on_hub
287
+ ):
284
288
  delete_storage_record(ssettings._uuid) # type: ignore
285
289
  raise e
286
290
  return None
@@ -12,10 +12,8 @@ if TYPE_CHECKING:
12
12
  from lamindb_setup.core.types import UPathStr
13
13
 
14
14
 
15
- def add_managed_storage(root: UPathStr, **fs_kwargs):
16
- """Add a remote default storage location to a local instance.
17
-
18
- This can be used to selectively share data.
15
+ def set_managed_storage(root: UPathStr, **fs_kwargs):
16
+ """Add or switch to another managed storage location.
19
17
 
20
18
  Args:
21
19
  root: `UPathStr` - The new storage root, e.g., an S3 bucket.
@@ -53,6 +53,11 @@ def _delete_storage_record(storage_uuid: UUID, client: Client) -> None:
53
53
  response = client.table("storage").delete().eq("id", storage_uuid.hex).execute()
54
54
  if response.data:
55
55
  logger.important(f"deleted storage record on hub {storage_uuid.hex}")
56
+ else:
57
+ raise PermissionError(
58
+ f"Deleting of storage with {storage_uuid.hex} was not successful. Probably, you"
59
+ " don't have sufficient permissions."
60
+ )
56
61
 
57
62
 
58
63
  def update_instance_record(instance_uuid: UUID, fields: dict) -> None:
@@ -100,7 +100,7 @@ def update_instance(instance_id: str, instance_fields: dict, client: Client):
100
100
  client.table("instance").update(instance_fields).eq("id", instance_id).execute()
101
101
  )
102
102
  if len(response.data) == 0:
103
- raise RuntimeError(
103
+ raise PermissionError(
104
104
  f"Update of instance with {instance_id} was not successful. Probably, you"
105
105
  " don't have sufficient permissions."
106
106
  )
@@ -187,3 +187,8 @@ def _delete_instance_record(instance_id: UUID, client: Client) -> None:
187
187
  response = client.table("instance").delete().eq("id", instance_id.hex).execute()
188
188
  if response.data:
189
189
  logger.important(f"deleted instance record on hub {instance_id.hex}")
190
+ else:
191
+ raise PermissionError(
192
+ f"Deleting of instance with {instance_id.hex} was not successful. Probably, you"
193
+ " don't have sufficient permissions."
194
+ )
@@ -89,6 +89,7 @@ def setup_instance_from_store(store: InstanceSettingsStore) -> InstanceSettings:
89
89
  db=store.db if store.db != "null" else None, # type: ignore
90
90
  schema=store.schema_str if store.schema_str != "null" else None,
91
91
  git_repo=store.git_repo if store.git_repo != "null" else None,
92
+ keep_artifacts_local=store.keep_artifacts_local, # type: ignore
92
93
  )
93
94
 
94
95
 
@@ -41,6 +41,8 @@ def save_settings(
41
41
  for store_key, type in type_hints.items():
42
42
  if type == Optional[str]:
43
43
  type = str
44
+ if type == Optional[bool]:
45
+ type = bool
44
46
  if "__" not in store_key:
45
47
  if store_key == "storage_root":
46
48
  value = settings.storage.root_as_str
@@ -177,7 +177,6 @@ class StorageSettings:
177
177
  # local storage
178
178
  self._has_local = False
179
179
  self._local = None
180
- self._is_on_hub: bool | None = None
181
180
 
182
181
  @property
183
182
  def id(self) -> int:
@@ -320,16 +319,10 @@ class StorageSettings:
320
319
 
321
320
  Only works if user has access to the instance.
322
321
  """
323
- if self._is_on_hub is None:
324
- from ._hub_client import call_with_fallback_auth
325
- from ._hub_crud import select_storage
326
-
327
- response = call_with_fallback_auth(select_storage, id=self._uuid.hex) # type: ignore
328
- if response is None:
329
- self._is_on_hub = False
330
- else:
331
- self._is_on_hub = True
332
- return self._is_on_hub
322
+ if self._uuid is None:
323
+ return False
324
+ else:
325
+ return True
333
326
 
334
327
  def key_to_filepath(self, filekey: Path | UPath | str) -> UPath:
335
328
  """Cloud or local filepath from filekey."""
@@ -57,6 +57,7 @@ class InstanceSettingsStore(BaseSettings):
57
57
  schema_str: Optional[str]
58
58
  id: str
59
59
  git_repo: Optional[str]
60
+ keep_artifacts_local: Optional[bool]
60
61
 
61
62
  class Config:
62
63
  env_prefix = "lamindb_instance_"
@@ -56,7 +56,10 @@ VALID_SUFFIXES = {
56
56
  ".zarr",
57
57
  ".json",
58
58
  }
59
-
59
+ VALID_COMPOSITE_SUFFIXES = {
60
+ ".anndata.zarr",
61
+ ".spatialdata.zarr",
62
+ }
60
63
 
61
64
  TRAILING_SEP = (os.sep, os.altsep) if os.altsep is not None else os.sep
62
65
 
@@ -74,6 +77,12 @@ def extract_suffix_from_path(path: Path, arg_name: str | None = None) -> str:
74
77
  total_suffix = "".join(path.suffixes)
75
78
  if total_suffix in VALID_SUFFIXES:
76
79
  return total_suffix
80
+ elif total_suffix.endswith(tuple(VALID_COMPOSITE_SUFFIXES)):
81
+ # below seems slow but OK for now
82
+ for suffix in VALID_COMPOSITE_SUFFIXES:
83
+ if total_suffix.endswith(suffix):
84
+ break
85
+ return suffix
77
86
  else:
78
87
  print_hint = True
79
88
  arg_name = "file" if arg_name is None else arg_name # for the warning
@@ -0,0 +1,10 @@
1
+ from __future__ import annotations
2
+
3
+ import lamindb_setup as ln_setup
4
+ import pytest
5
+ from lamindb_setup._connect_instance import InstanceNotFoundError
6
+
7
+
8
+ def test_delete_invalid_name():
9
+ with pytest.raises(InstanceNotFoundError):
10
+ ln_setup.delete("invalid/name")
@@ -5,6 +5,7 @@ from uuid import UUID
5
5
 
6
6
  import lamindb_setup as ln_setup
7
7
  import pytest
8
+ from lamindb_setup._connect_instance import InstanceNotFoundError
8
9
  from lamindb_setup.core._hub_client import connect_hub_with_auth
9
10
  from lamindb_setup.core._hub_core import _connect_instance
10
11
  from lamindb_setup.core._hub_crud import (
@@ -27,7 +28,10 @@ def get_hub_client():
27
28
  def test_init_instance_postgres_default_name(get_hub_client):
28
29
  hub = get_hub_client
29
30
  instance_name = "pgtest"
30
- ln_setup.delete(instance_name, force=True)
31
+ try:
32
+ ln_setup.delete(instance_name, force=True)
33
+ except InstanceNotFoundError:
34
+ pass
31
35
  # now, run init
32
36
  ln_setup.init(storage="./mydatapg", db=pgurl, _test=True)
33
37
  assert ln_setup.settings.instance.slug == "testuser2/pgtest"
@@ -0,0 +1,15 @@
1
+ import lamindb_setup as ln_setup
2
+ import pytest
3
+ from lamindb_setup._connect_instance import InstanceNotFoundError
4
+ from lamindb_setup._set_managed_storage import set_managed_storage
5
+
6
+
7
+ def test_set_storage_sqlite():
8
+ try:
9
+ ln_setup.delete("mydata", force=True)
10
+ except InstanceNotFoundError:
11
+ pass
12
+ ln_setup.init(storage="./mydata", _test=True)
13
+ with pytest.raises(ValueError):
14
+ set_managed_storage("mydata2")
15
+ ln_setup.delete("mydata", force=True)
@@ -19,6 +19,7 @@ def test_extract_suffix_from_path():
19
19
  ("logs/date.log.123", ""), # digits are no valid suffixes
20
20
  ("salmon.merged.gene_counts.tsv", ".tsv"),
21
21
  ("salmon.merged.gene_counts.tsv.gz", ".tsv.gz"),
22
+ ("filename.v1.1.0.spatialdata.zarr", ".spatialdata.zarr"),
22
23
  ]
23
24
  for path, suffix in collection:
24
25
  filepath = Path(path)
@@ -1,13 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import lamindb_setup as ln_setup
4
- import pytest
5
-
6
-
7
- def test_delete_invalid_name():
8
- with pytest.raises(ValueError) as error:
9
- ln_setup.delete("invalid/name")
10
- assert (
11
- error.exconly()
12
- == "ValueError: Invalid instance name: '/' delimiter not allowed."
13
- )
@@ -1,11 +0,0 @@
1
- import lamindb_setup as ln_setup
2
- import pytest
3
- from lamindb_setup._add_remote_storage import add_managed_storage
4
-
5
-
6
- def test_set_storage_sqlite():
7
- ln_setup.delete("mydata", force=True)
8
- ln_setup.init(storage="./mydata", _test=True)
9
- with pytest.raises(ValueError):
10
- add_managed_storage("mydata2")
11
- ln_setup.delete("mydata", force=True)
File without changes
File without changes