lamindb_setup 1.10.0__tar.gz → 1.10.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 (108) hide show
  1. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/PKG-INFO +2 -2
  2. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-cloud/02-connect-local-instance.ipynb +15 -11
  3. lamindb_setup-1.10.1/docs/hub-cloud/09-test-migrate.ipynb +94 -0
  4. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-prod/test-import-schema.ipynb +3 -4
  5. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/__init__.py +1 -1
  6. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_connect_instance.py +34 -19
  7. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_migrate.py +7 -7
  8. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_deprecated.py +1 -1
  9. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_hub_core.py +30 -7
  10. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_hub_crud.py +40 -20
  11. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_settings.py +4 -14
  12. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_settings_instance.py +32 -0
  13. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/django.py +3 -7
  14. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/pyproject.toml +1 -1
  15. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-cloud/test_connect_instance.py +4 -0
  16. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-cloud/test_init_instance.py +3 -1
  17. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-local/scripts/script-connect-fine-grained-access.py +4 -1
  18. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-local/test_all.py +93 -53
  19. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-prod/test_django.py +0 -4
  20. lamindb_setup-1.10.1/tests/hub-prod/test_migrate.py +13 -0
  21. lamindb_setup-1.10.0/tests/hub-cloud/test_migrate.py +0 -22
  22. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/.github/workflows/build.yml +0 -0
  23. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/.github/workflows/doc-changes.yml +0 -0
  24. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/.gitignore +0 -0
  25. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/.pre-commit-config.yaml +0 -0
  26. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/LICENSE +0 -0
  27. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/README.md +0 -0
  28. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/changelog.md +0 -0
  29. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-cloud/01-init-local-instance.ipynb +0 -0
  30. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-cloud/03-add-managed-storage.ipynb +0 -0
  31. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-cloud/04-test-bionty.ipynb +0 -0
  32. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-cloud/05-init-hosted-instance.ipynb +0 -0
  33. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-cloud/06-connect-hosted-instance.ipynb +0 -0
  34. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-cloud/07-keep-artifacts-local.ipynb +0 -0
  35. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-cloud/08-test-multi-session.ipynb +0 -0
  36. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-cloud/test_notebooks.py +0 -0
  37. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-prod/test-cache-management.ipynb +0 -0
  38. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-prod/test-cloud-sync.ipynb +0 -0
  39. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-prod/test-connect-anonymously.ipynb +0 -0
  40. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-prod/test-empty-init.ipynb +0 -0
  41. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-prod/test-init-load-local-anonymously.ipynb +0 -0
  42. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-prod/test-insufficient-user-info.ipynb +0 -0
  43. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-prod/test-invalid-schema.ipynb +0 -0
  44. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-prod/test-sqlite-lock.ipynb +0 -0
  45. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/hub-prod/test_notebooks2.py +0 -0
  46. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/index.md +0 -0
  47. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/notebooks.md +0 -0
  48. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/docs/reference.md +0 -0
  49. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_cache.py +0 -0
  50. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_check.py +0 -0
  51. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_check_setup.py +0 -0
  52. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_delete.py +0 -0
  53. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_disconnect.py +0 -0
  54. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_django.py +0 -0
  55. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_entry_points.py +0 -0
  56. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_exportdb.py +0 -0
  57. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_importdb.py +0 -0
  58. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_init_instance.py +0 -0
  59. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_register_instance.py +0 -0
  60. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_schema.py +0 -0
  61. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_schema_metadata.py +0 -0
  62. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_set_managed_storage.py +0 -0
  63. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_setup_user.py +0 -0
  64. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/_silence_loggers.py +0 -0
  65. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/__init__.py +0 -0
  66. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_aws_options.py +0 -0
  67. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_aws_storage.py +0 -0
  68. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_docs.py +0 -0
  69. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_hub_client.py +0 -0
  70. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_hub_utils.py +0 -0
  71. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_private_django_api.py +0 -0
  72. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_settings_load.py +0 -0
  73. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_settings_save.py +0 -0
  74. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_settings_storage.py +0 -0
  75. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_settings_store.py +0 -0
  76. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_settings_user.py +0 -0
  77. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/_setup_bionty_sources.py +0 -0
  78. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/cloud_sqlite_locker.py +0 -0
  79. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/exceptions.py +0 -0
  80. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/hashing.py +0 -0
  81. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/types.py +0 -0
  82. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/core/upath.py +0 -0
  83. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/errors.py +0 -0
  84. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/py.typed +0 -0
  85. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/lamindb_setup/types.py +0 -0
  86. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/noxfile.py +0 -0
  87. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-cloud/scripts/script-init-pass-user-no-writes.py +0 -0
  88. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-cloud/scripts/script-to-fail-managed-storage.py +0 -0
  89. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-cloud/test_delete_instance.py +0 -0
  90. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-cloud/test_edge_request.py +0 -0
  91. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-cloud/test_fail_managed_storage.py +0 -0
  92. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-cloud/test_init_pass_user_no_writes.py +0 -0
  93. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-cloud/test_login.py +0 -0
  94. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-cloud/test_set_storage.py +0 -0
  95. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-local/conftest.py +0 -0
  96. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-local/test_update_schema_in_hub.py +0 -0
  97. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-prod/conftest.py +0 -0
  98. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-prod/test_aws_options_manager.py +0 -0
  99. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-prod/test_global_settings.py +0 -0
  100. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-prod/test_switch_and_fallback_env.py +0 -0
  101. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/hub-prod/test_upath.py +0 -0
  102. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/storage/test_entry_point.py +0 -0
  103. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/storage/test_hashing.py +0 -0
  104. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/storage/test_storage_access.py +0 -0
  105. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/storage/test_storage_basis.py +0 -0
  106. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/storage/test_storage_settings.py +0 -0
  107. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/storage/test_storage_stats.py +0 -0
  108. {lamindb_setup-1.10.0 → lamindb_setup-1.10.1}/tests/storage/test_to_url.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lamindb_setup
3
- Version: 1.10.0
3
+ Version: 1.10.1
4
4
  Summary: Setup & configure LaminDB.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Requires-Python: >=3.10
@@ -27,7 +27,7 @@ Requires-Dist: psycopg2-binary ; extra == "dev"
27
27
  Requires-Dist: python-dotenv ; extra == "dev"
28
28
  Requires-Dist: nox ; extra == "dev"
29
29
  Requires-Dist: pytest>=6.0 ; extra == "dev"
30
- Requires-Dist: pytest-cov ; extra == "dev"
30
+ Requires-Dist: pytest-cov<7.0.0 ; extra == "dev"
31
31
  Requires-Dist: pytest-xdist ; extra == "dev"
32
32
  Requires-Dist: nbproject-test>=0.4.3 ; extra == "dev"
33
33
  Requires-Dist: pandas ; extra == "dev"
@@ -60,10 +60,7 @@
60
60
  "metadata": {},
61
61
  "outputs": [],
62
62
  "source": [
63
- "# bionty is not in the (schema) modules of mydata\n",
64
- "# _check_instance_setup is called inside with from_module=None\n",
65
- "# the branch where django is not setup yet\n",
66
- "# as from_module=None it won't connect to an instance here\n",
63
+ "# no instance connected, prompts to connect\n",
67
64
  "import bionty"
68
65
  ]
69
66
  },
@@ -79,6 +76,17 @@
79
76
  ") # also test passing _reload_lamindb explicitly"
80
77
  ]
81
78
  },
79
+ {
80
+ "cell_type": "code",
81
+ "execution_count": null,
82
+ "id": "5a59b8d0",
83
+ "metadata": {},
84
+ "outputs": [],
85
+ "source": [
86
+ "# this is a local intstance, no spaces needed\n",
87
+ "assert ln_setup.settings.instance.available_spaces is None"
88
+ ]
89
+ },
82
90
  {
83
91
  "cell_type": "code",
84
92
  "execution_count": null,
@@ -88,8 +96,7 @@
88
96
  "source": [
89
97
  "# wetlab is not in the (schema) modules of mydata\n",
90
98
  "with pytest.raises(ModuleWasntConfigured):\n",
91
- " # _check_instance_setup is called inside with from_module=None\n",
92
- " # the branch where django is setup\n",
99
+ " # _check_instance_setup is called inside\n",
93
100
  " import wetlab"
94
101
  ]
95
102
  },
@@ -100,12 +107,9 @@
100
107
  "metadata": {},
101
108
  "outputs": [],
102
109
  "source": [
103
- "# wetlab is not in the (schema) modules of mydata\n",
110
+ "# bionty is not in the (schema) modules of mydata\n",
104
111
  "with pytest.raises(ModuleWasntConfigured):\n",
105
- " # _check_instance_setup is called inside with from_module=\"bionty\"\n",
106
- " # the branch where django is setup\n",
107
- " # in __getattr__ in __init__.py\n",
108
- " bionty.CellType"
112
+ " import bionty"
109
113
  ]
110
114
  },
111
115
  {
@@ -0,0 +1,94 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "5acb0094",
6
+ "metadata": {},
7
+ "source": [
8
+ "# Migrate a hosted instance"
9
+ ]
10
+ },
11
+ {
12
+ "cell_type": "code",
13
+ "execution_count": null,
14
+ "id": "213594a4",
15
+ "metadata": {},
16
+ "outputs": [],
17
+ "source": [
18
+ "import lamindb_setup as ln_setup"
19
+ ]
20
+ },
21
+ {
22
+ "cell_type": "code",
23
+ "execution_count": null,
24
+ "id": "0a310f89",
25
+ "metadata": {},
26
+ "outputs": [],
27
+ "source": [
28
+ "ln_setup.login(\"testuser1\")"
29
+ ]
30
+ },
31
+ {
32
+ "cell_type": "code",
33
+ "execution_count": null,
34
+ "id": "eecceaf0",
35
+ "metadata": {},
36
+ "outputs": [],
37
+ "source": [
38
+ "ln_setup.connect(\"laminlabs/lamindata\", use_root_db_user=True)"
39
+ ]
40
+ },
41
+ {
42
+ "cell_type": "code",
43
+ "execution_count": null,
44
+ "id": "ffb9b496",
45
+ "metadata": {},
46
+ "outputs": [],
47
+ "source": [
48
+ "# double connect to check migration with django reset\n",
49
+ "ln_setup.connect(\"laminlabs/lamindata\", use_root_db_user=True)"
50
+ ]
51
+ },
52
+ {
53
+ "cell_type": "code",
54
+ "execution_count": null,
55
+ "id": "e9887c91",
56
+ "metadata": {},
57
+ "outputs": [],
58
+ "source": [
59
+ "assert \"root\" in ln_setup.settings.instance.db"
60
+ ]
61
+ },
62
+ {
63
+ "cell_type": "code",
64
+ "execution_count": null,
65
+ "id": "657de4c6",
66
+ "metadata": {},
67
+ "outputs": [],
68
+ "source": [
69
+ "ln_setup.migrate.deploy()"
70
+ ]
71
+ }
72
+ ],
73
+ "metadata": {
74
+ "kernelspec": {
75
+ "display_name": "Python 3 (ipykernel)",
76
+ "language": "python",
77
+ "name": "python3"
78
+ },
79
+ "language_info": {
80
+ "codemirror_mode": {
81
+ "name": "ipython",
82
+ "version": 3
83
+ },
84
+ "file_extension": ".py",
85
+ "mimetype": "text/x-python",
86
+ "name": "python",
87
+ "nbconvert_exporter": "python",
88
+ "pygments_lexer": "ipython3",
89
+ "version": "3.10.16"
90
+ }
91
+ },
92
+ "nbformat": 4,
93
+ "nbformat_minor": 5
94
+ }
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "cells": [
3
3
  {
4
- "attachments": {},
5
4
  "cell_type": "markdown",
6
5
  "id": "261ca5e4-f46f-4361-9c3e-f944bbe14484",
7
6
  "metadata": {},
@@ -16,7 +15,7 @@
16
15
  "source": [
17
16
  "Also see the corresponding FAQ notebook in lamindb: `import-modules`.\n",
18
17
  "\n",
19
- "If you try to access an attribute (other than `model`), you'll load the instance in the same way as calling `import lamindb`."
18
+ "You'll load the instance in the same way as calling `import lamindb` when you import a schema module."
20
19
  ]
21
20
  },
22
21
  {
@@ -60,7 +59,7 @@
60
59
  "metadata": {},
61
60
  "outputs": [],
62
61
  "source": [
63
- "assert not lamindb_setup.core.django.IS_SETUP"
62
+ "assert lamindb_setup.core.django.IS_SETUP"
64
63
  ]
65
64
  },
66
65
  {
@@ -110,7 +109,7 @@
110
109
  "name": "python",
111
110
  "nbconvert_exporter": "python",
112
111
  "pygments_lexer": "ipython3",
113
- "version": "3.10.13"
112
+ "version": "3.10.16"
114
113
  },
115
114
  "nbproject": {
116
115
  "id": "2lhqA4uTKSFP",
@@ -35,7 +35,7 @@ Modules & settings:
35
35
 
36
36
  """
37
37
 
38
- __version__ = "1.10.0" # denote a release candidate for 0.1.0 with 0.1rc1
38
+ __version__ = "1.10.1" # denote a release candidate for 0.1.0 with 0.1rc1
39
39
 
40
40
  import os
41
41
 
@@ -24,6 +24,7 @@ from .core._settings_load import load_instance_settings
24
24
  from .core._settings_storage import StorageSettings
25
25
  from .core._settings_store import instance_settings_file, settings_dir
26
26
  from .core.cloud_sqlite_locker import unlock_cloud_sqlite_upon_exception
27
+ from .core.django import reset_django
27
28
  from .errors import CannotSwitchDefaultInstance
28
29
 
29
30
  if TYPE_CHECKING:
@@ -101,6 +102,7 @@ def _connect_instance(
101
102
  *,
102
103
  db: str | None = None,
103
104
  raise_permission_error: bool = True,
105
+ use_root_db_user: bool = False,
104
106
  access_token: str | None = None,
105
107
  ) -> InstanceSettings:
106
108
  settings_file = instance_settings_file(name, owner)
@@ -120,7 +122,10 @@ def _connect_instance(
120
122
  # do not call hub if the user is anonymous
121
123
  if owner != "anonymous":
122
124
  hub_result = connect_instance_hub(
123
- owner=owner, name=name, access_token=access_token
125
+ owner=owner,
126
+ name=name,
127
+ access_token=access_token,
128
+ use_root_db_user=use_root_db_user,
124
129
  )
125
130
  else:
126
131
  hub_result = "anonymous-user"
@@ -155,7 +160,9 @@ def _connect_instance(
155
160
  schema_id=None
156
161
  if (schema_id := instance_result["schema_id"]) is None
157
162
  else UUID(schema_id),
158
- fine_grained_access=instance_result.get("fine_grained_access", False),
163
+ fine_grained_access=bool(
164
+ instance_result["fine_grained_access"]
165
+ ), # can be None
159
166
  db_permissions=instance_result.get("db_permissions", None),
160
167
  )
161
168
  else:
@@ -186,6 +193,8 @@ def reset_django_module_variables():
186
193
  # to the old classes
187
194
  # There doesn't seem to be an easy way to fix this problem
188
195
 
196
+ logger.important_hint("resetting django module variables")
197
+
189
198
  import types
190
199
 
191
200
  from django.apps import apps
@@ -230,12 +239,11 @@ def reset_django_module_variables():
230
239
  continue
231
240
 
232
241
 
233
- def _connect_cli(instance: str) -> None:
242
+ def _connect_cli(instance: str, use_root_db_user: bool = False) -> None:
234
243
  from lamindb_setup import settings as settings_
235
244
 
236
- settings_.auto_connect = True
237
245
  owner, name = get_owner_name_from_identifier(instance)
238
- isettings = _connect_instance(owner, name)
246
+ isettings = _connect_instance(owner, name, use_root_db_user=use_root_db_user)
239
247
  isettings._persist(write_to_disk=True)
240
248
  if not isettings.is_on_hub or isettings._is_cloud_sqlite:
241
249
  # there are two reasons to call the full-blown connect
@@ -262,6 +270,7 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
262
270
  """
263
271
  # validate kwargs
264
272
  valid_kwargs = {
273
+ "use_root_db_user",
265
274
  "_db",
266
275
  "_write_settings",
267
276
  "_raise_not_found_error",
@@ -274,6 +283,7 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
274
283
  raise TypeError(f"connect() got unexpected keyword argument '{kwarg}'")
275
284
  isettings: InstanceSettings = None # type: ignore
276
285
  # _db is still needed because it is called in init
286
+ use_root_db_user: bool = kwargs.get("use_root_db_user", False)
277
287
  _db: str | None = kwargs.get("_db", None)
278
288
  _write_settings: bool = kwargs.get("_write_settings", False)
279
289
  _raise_not_found_error: bool = kwargs.get("_raise_not_found_error", True)
@@ -289,12 +299,18 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
289
299
 
290
300
  try:
291
301
  if instance is None:
292
- isettings_or_none = _get_current_instance_settings()
293
- if isettings_or_none is None:
294
- raise ValueError(
295
- "No instance was connected through the CLI, pass a value to `instance` or connect via the CLI."
296
- )
297
- isettings = isettings_or_none
302
+ if settings._instance_exists:
303
+ isettings = settings.instance
304
+ else:
305
+ isettings_or_none = _get_current_instance_settings()
306
+ if isettings_or_none is None:
307
+ raise ValueError(
308
+ "No instance was connected through the CLI, pass a value to `instance` or connect via the CLI."
309
+ )
310
+ isettings = isettings_or_none
311
+ if use_root_db_user:
312
+ reset_django()
313
+ owner, name = isettings.owner, isettings.name
298
314
  if _db is not None and isettings.dialect == "postgresql":
299
315
  isettings._db = _db
300
316
  else:
@@ -310,14 +326,13 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
310
326
  # could be made more specific by checking whether the django
311
327
  # configured database is the same as the one in settings
312
328
  and connection.settings_dict["NAME"] != ":memory:"
329
+ and not use_root_db_user # always re-connect for root db user
313
330
  ):
314
331
  logger.important(
315
332
  f"doing nothing, already connected lamindb: {settings.instance.slug}"
316
333
  )
317
334
  return None
318
335
  else:
319
- from lamindb_setup.core.django import reset_django
320
-
321
336
  if (
322
337
  settings._instance_exists
323
338
  and settings.instance.slug != "none/none"
@@ -328,11 +343,6 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
328
343
  raise CannotSwitchDefaultInstance(
329
344
  "Cannot switch default instance while `ln.track()` is live: call `ln.finish()`"
330
345
  )
331
- else:
332
- logger.important_hint(
333
- "switching the default lamindb instance might produce unexpected side effects with function-scoped imports: "
334
- "please import lamindb at the module level instead of inside functions"
335
- )
336
346
  reset_django()
337
347
  elif (
338
348
  _write_settings
@@ -341,9 +351,14 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
341
351
  ):
342
352
  disconnect(mute=True)
343
353
 
354
+ if instance is not None or use_root_db_user:
344
355
  try:
345
356
  isettings = _connect_instance(
346
- owner, name, db=_db, access_token=access_token
357
+ owner,
358
+ name,
359
+ db=_db,
360
+ access_token=access_token,
361
+ use_root_db_user=use_root_db_user,
347
362
  )
348
363
  except InstanceNotFoundError as e:
349
364
  if _raise_not_found_error:
@@ -93,17 +93,13 @@ class migrate:
93
93
  @disable_auto_connect
94
94
  def create(cls) -> None:
95
95
  """Create a migration."""
96
- if _check_instance_setup():
97
- raise RuntimeError("Restart Python session to create migration or use CLI!")
98
96
  setup_django(settings.instance, create_migrations=True)
99
97
 
100
98
  @classmethod
101
99
  def deploy(cls, package_name: str | None = None, number: int | None = None) -> None:
102
100
  """Deploy a migration."""
103
- from ._schema_metadata import update_schema_in_hub
104
-
105
- if _check_instance_setup():
106
- raise RuntimeError("Restart Python session to migrate or use CLI!")
101
+ from lamindb_setup._connect_instance import connect
102
+ from lamindb_setup._schema_metadata import update_schema_in_hub
107
103
  from lamindb_setup.core._hub_client import call_with_fallback_auth
108
104
  from lamindb_setup.core._hub_crud import (
109
105
  select_collaborator,
@@ -117,12 +113,17 @@ class migrate:
117
113
  select_collaborator,
118
114
  instance_id=settings.instance._id,
119
115
  account_id=settings.user._uuid,
116
+ fine_grained_access=settings.instance._fine_grained_access,
120
117
  )
121
118
  if collaborator is None or collaborator["role"] != "admin":
122
119
  raise SystemExit(
123
120
  "❌ Only admins can deploy migrations, please ensure that you're an"
124
121
  f" admin: https://lamin.ai/{settings.instance.slug}/settings"
125
122
  )
123
+ # ensure we connect with the root user
124
+ if "root" not in settings.instance.db:
125
+ connect(use_root_db_user=True)
126
+ assert "root" in (instance_db := settings.instance.db), instance_db
126
127
  # we need lamindb to be installed, otherwise we can't populate the version
127
128
  # information in the hub
128
129
  import lamindb
@@ -139,7 +140,6 @@ class migrate:
139
140
  # this populates the hub
140
141
  if settings.instance.is_on_hub:
141
142
  logger.important(f"updating lamindb version in hub: {lamindb.__version__}")
142
- # TODO: integrate update of instance table within update_schema_in_hub & below
143
143
  if settings.instance.dialect != "sqlite":
144
144
  update_schema_in_hub()
145
145
  call_with_fallback_auth(
@@ -51,7 +51,7 @@ def deprecated(new_name: str):
51
51
  warnings.warn(
52
52
  f"Use {new_name} instead of {func.__name__}, "
53
53
  f"{func.__name__} will be removed in the future.",
54
- category=FutureWarning,
54
+ category=DeprecationWarning,
55
55
  stacklevel=2,
56
56
  )
57
57
  return func(*args, **kwargs)
@@ -424,6 +424,7 @@ def _init_instance_hub(
424
424
  def _connect_instance_hub(
425
425
  owner: str, # account_handle
426
426
  name: str, # instance_name
427
+ use_root_db_user: bool,
427
428
  client: Client,
428
429
  ) -> tuple[dict, dict] | str:
429
430
  response = client.functions.invoke(
@@ -475,17 +476,29 @@ def _connect_instance_hub(
475
476
 
476
477
  if instance["db_scheme"] is not None:
477
478
  db_user_name, db_user_password = None, None
478
- if "db_user_name" in instance and "db_user_password" in instance:
479
+ if (
480
+ "db_user_name" in instance
481
+ and "db_user_password" in instance
482
+ and not use_root_db_user
483
+ ):
479
484
  db_user_name, db_user_password = (
480
485
  instance["db_user_name"],
481
486
  instance["db_user_password"],
482
487
  )
483
488
  else:
484
- db_user = select_db_user_by_instance(instance["id"], client)
489
+ if use_root_db_user:
490
+ fine_grained_access = False
491
+ else:
492
+ fine_grained_access = bool(
493
+ instance["fine_grained_access"]
494
+ ) # can be None
495
+ db_user = select_db_user_by_instance(
496
+ instance["id"], fine_grained_access, client
497
+ )
485
498
  if db_user is not None:
486
499
  db_user_name, db_user_password = (
487
- db_user["db_user_name"],
488
- db_user["db_user_password"],
500
+ db_user["name" if fine_grained_access else "db_user_name"],
501
+ db_user["password" if fine_grained_access else "db_user_password"],
489
502
  )
490
503
  db_dsn = LaminDsn.build(
491
504
  scheme=instance["db_scheme"],
@@ -505,15 +518,25 @@ def connect_instance_hub(
505
518
  owner: str, # account_handle
506
519
  name: str, # instance_name
507
520
  access_token: str | None = None,
521
+ use_root_db_user: bool = False,
508
522
  ) -> tuple[dict, dict] | str:
509
523
  from ._settings import settings
510
524
 
511
525
  if settings.user.handle != "anonymous" or access_token is not None:
512
526
  return call_with_fallback_auth(
513
- _connect_instance_hub, owner=owner, name=name, access_token=access_token
527
+ _connect_instance_hub,
528
+ owner=owner,
529
+ name=name,
530
+ use_root_db_user=use_root_db_user,
531
+ access_token=access_token,
514
532
  )
515
533
  else:
516
- return call_with_fallback(_connect_instance_hub, owner=owner, name=name)
534
+ return call_with_fallback(
535
+ _connect_instance_hub,
536
+ owner=owner,
537
+ name=name,
538
+ use_root_db_user=use_root_db_user,
539
+ )
517
540
 
518
541
 
519
542
  def access_aws(storage_root: str, access_token: str | None = None) -> dict[str, dict]:
@@ -570,7 +593,7 @@ def access_db(
570
593
  if isinstance(instance, InstanceSettings):
571
594
  instance_id = instance._id
572
595
  instance_slug = instance.slug
573
- instance_api_url = instance._api_url
596
+ instance_api_url = instance.api_url
574
597
  else:
575
598
  instance_id = UUID(instance["id"])
576
599
  instance_slug = instance["owner"] + "/" + instance["name"]
@@ -137,10 +137,12 @@ def update_instance(instance_id: str, instance_fields: dict, client: Client):
137
137
  def select_collaborator(
138
138
  instance_id: str,
139
139
  account_id: str,
140
+ fine_grained_access: bool,
140
141
  client: Client,
141
142
  ):
143
+ table = "access_instance" if fine_grained_access else "account_instance"
142
144
  data = (
143
- client.table("account_instance")
145
+ client.table(table)
144
146
  .select("*")
145
147
  .eq("instance_id", instance_id)
146
148
  .eq("account_id", account_id)
@@ -180,35 +182,53 @@ def insert_db_user(
180
182
  db_user_name: str,
181
183
  db_user_password: str,
182
184
  instance_id: UUID,
185
+ fine_grained_access: bool,
183
186
  client: Client,
184
187
  ) -> None:
185
- fields = (
186
- {
187
- "id": uuid4().hex,
188
- "instance_id": instance_id.hex,
189
- "name": name,
190
- "db_user_name": db_user_name,
191
- "db_user_password": db_user_password,
192
- },
193
- )
194
- data = client.table("db_user").insert(fields).execute().data
188
+ fields = {"instance_id": instance_id.hex}
189
+ if fine_grained_access:
190
+ table = "access_db_user"
191
+ fields.update(
192
+ {
193
+ "name": db_user_name,
194
+ "password": db_user_password,
195
+ "type": name,
196
+ }
197
+ )
198
+ else:
199
+ table = "db_user"
200
+ fields.update(
201
+ {
202
+ "id": uuid4().hex,
203
+ "name": name,
204
+ "db_user_name": db_user_name,
205
+ "db_user_password": db_user_password,
206
+ }
207
+ )
208
+
209
+ data = client.table(table).insert(fields).execute().data
195
210
  return data[0]
196
211
 
197
212
 
198
- def select_db_user_by_instance(instance_id: str, client: Client):
213
+ def select_db_user_by_instance(
214
+ instance_id: str, fine_grained_access: bool, client: Client
215
+ ):
199
216
  """Get db_user for which client has permission."""
200
- data = (
201
- client.table("db_user")
202
- .select("*")
203
- .eq("instance_id", instance_id)
204
- .execute()
205
- .data
206
- )
217
+ if fine_grained_access:
218
+ table = "access_db_user"
219
+ type_name = "type"
220
+ type_priority = "jwt"
221
+ else:
222
+ table = "db_user"
223
+ type_name = "name"
224
+ type_priority = "write"
225
+
226
+ data = client.table(table).select("*").eq("instance_id", instance_id).execute().data
207
227
  if len(data) == 0:
208
228
  return None
209
229
  elif len(data) > 1:
210
230
  for item in data:
211
- if item["name"] == "write":
231
+ if item[type_name] == type_priority:
212
232
  return item
213
233
  logger.warning("found multiple db credentials, using the first one")
214
234
  return data[0]
@@ -79,24 +79,15 @@ class SetupSettings:
79
79
  def auto_connect(self) -> bool:
80
80
  """Auto-connect to current instance upon `import lamindb`.
81
81
 
82
- Upon installing `lamindb`, this setting is `False`.
83
-
84
- Upon calling `lamin init` or `lamin connect` on the CLI, this setting is switched to `True`.
85
-
86
- `ln.connect()` doesn't change the value of this setting.
87
-
88
- You can manually change this setting
89
-
90
- - in Python: `ln.setup.settings.auto_connect = True/False`
91
- - via the CLI: `lamin settings set auto-connect true/false`
82
+ This setting is always `True` and will be removed in a future version.
92
83
  """
93
84
  return True
94
85
 
95
86
  @auto_connect.setter
96
87
  def auto_connect(self, value: bool) -> None:
97
- # logger.warning(
98
- # "setting auto_connect to `False` no longer has an effect and the setting will likely be removed in the future; since lamindb 1.7, auto_connect `True` no longer clashes with connecting in a Python session",
99
- # )
88
+ logger.warning(
89
+ "setting auto_connect to `False` no longer has an effect and the setting will likely be removed in the future",
90
+ )
100
91
  if value:
101
92
  self._auto_connect_path.touch()
102
93
  else:
@@ -324,7 +315,6 @@ class SetupSettings:
324
315
  else:
325
316
  repr += "Current instance: None"
326
317
  repr += "\nConfig:\n"
327
- repr += f" - auto-connect in Python: {self.auto_connect}\n"
328
318
  repr += f" - private Django API: {self.private_django_api}\n"
329
319
  repr += "Local directories:\n"
330
320
  repr += f" - cache: {self.cache_dir.as_posix()}\n"
@@ -5,6 +5,7 @@ import shutil
5
5
  from pathlib import Path
6
6
  from typing import TYPE_CHECKING, Literal
7
7
 
8
+ from django.db import connection
8
9
  from django.db.utils import ProgrammingError
9
10
  from lamin_utils import logger
10
11
 
@@ -343,6 +344,37 @@ class InstanceSettings:
343
344
  """
344
345
  return self._git_repo
345
346
 
347
+ @property
348
+ def api_url(self) -> str | None:
349
+ """URL for REST API.
350
+
351
+ Use this URL for API calls related to this instance.
352
+ """
353
+ return self._api_url
354
+
355
+ @property
356
+ def available_spaces(self) -> dict | None:
357
+ """Available spaces with roles for instances fine-grained permissions.
358
+
359
+ Returns a dictionary with roles as keys and lists of available spaces
360
+ as values if this instance has fine-grained permissions and the current user
361
+ is a collaborator, `None` otherwise.
362
+ """
363
+ if self._db_permissions != "jwt":
364
+ return None
365
+
366
+ from lamindb.models import Space
367
+
368
+ spaces: dict = {"admin": [], "write": [], "read": []}
369
+ with connection.cursor() as cur:
370
+ cur.execute("SELECT * FROM check_access() WHERE type = 'space'")
371
+ rows = cur.fetchall()
372
+ for row in rows:
373
+ spaces[row[1]].append(row[0])
374
+ return {
375
+ k: Space.filter(id__in=v).to_list() if v else [] for k, v in spaces.items()
376
+ }
377
+
346
378
  @property
347
379
  def _id(self) -> UUID:
348
380
  """The internal instance id."""