lamindb_setup 1.15.2__tar.gz → 1.17.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 (116) hide show
  1. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/.github/workflows/build.yml +58 -5
  2. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/.gitignore +1 -0
  3. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/PKG-INFO +5 -5
  4. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/__init__.py +6 -3
  5. lamindb_setup-1.17.0/lamindb_setup/_check_setup.py +131 -0
  6. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_connect_instance.py +9 -23
  7. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_delete.py +10 -5
  8. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_disconnect.py +12 -9
  9. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_init_instance.py +0 -1
  10. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_migrate.py +0 -14
  11. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_schema_metadata.py +9 -11
  12. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_setup_user.py +28 -7
  13. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_silence_loggers.py +2 -0
  14. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_aws_options.py +17 -7
  15. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_clone.py +1 -1
  16. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_hub_client.py +1 -2
  17. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_hub_core.py +9 -6
  18. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_private_django_api.py +0 -1
  19. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings.py +14 -10
  20. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_instance.py +28 -6
  21. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_load.py +25 -7
  22. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_storage.py +3 -1
  23. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/django.py +48 -18
  24. lamindb_setup-1.17.0/lamindb_setup/core/lamin.db.gz +0 -0
  25. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/upath.py +34 -13
  26. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/errors.py +0 -12
  27. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/io.py +69 -33
  28. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/noxfile.py +18 -3
  29. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/pyproject.toml +3 -3
  30. lamindb_setup-1.17.0/tests/connectivity/conftest.py +33 -0
  31. lamindb_setup-1.17.0/tests/connectivity/test_proxies_certificates.py +58 -0
  32. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/test_clone_instance.py +3 -3
  33. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/test_connect_instance.py +15 -6
  34. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/test_edge_request.py +1 -1
  35. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/test_init_instance.py +1 -0
  36. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/test_login.py +3 -8
  37. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-local/conftest.py +8 -2
  38. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-local/test_all.py +2 -2
  39. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-local/test_update_schema_in_hub.py +10 -6
  40. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-prod/test_switch_and_fallback_env.py +2 -2
  41. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/storage/test_db_import_export.py +160 -2
  42. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/storage/test_storage_access.py +4 -8
  43. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/storage/test_storage_basis.py +1 -0
  44. lamindb_setup-1.15.2/docs/hub-prod/test-insufficient-user-info.ipynb +0 -192
  45. lamindb_setup-1.15.2/lamindb_setup/_check_setup.py +0 -192
  46. lamindb_setup-1.15.2/tests/storage/test_httpx_client.py +0 -21
  47. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/.github/workflows/doc-changes.yml +0 -0
  48. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/.pre-commit-config.yaml +0 -0
  49. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/LICENSE +0 -0
  50. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/README.md +0 -0
  51. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/changelog.md +0 -0
  52. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-cloud/01-init-local-instance.ipynb +0 -0
  53. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-cloud/02-connect-local-instance.ipynb +0 -0
  54. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-cloud/03-add-managed-storage.ipynb +0 -0
  55. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-cloud/04-test-bionty.ipynb +0 -0
  56. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-cloud/05-init-hosted-instance.ipynb +0 -0
  57. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-cloud/06-connect-hosted-instance.ipynb +0 -0
  58. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-cloud/07-keep-artifacts-local.ipynb +0 -0
  59. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-cloud/08-test-multi-session.ipynb +0 -0
  60. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-cloud/09-test-migrate.ipynb +0 -0
  61. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-cloud/test_notebooks.py +0 -0
  62. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-prod/test-cache-management.ipynb +0 -0
  63. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-prod/test-cloud-sync.ipynb +0 -0
  64. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-prod/test-connect-anonymously.ipynb +0 -0
  65. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-prod/test-empty-init.ipynb +0 -0
  66. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-prod/test-import-schema.ipynb +0 -0
  67. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-prod/test-init-load-local-anonymously.ipynb +0 -0
  68. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-prod/test-invalid-schema.ipynb +0 -0
  69. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-prod/test-sqlite-lock.ipynb +0 -0
  70. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/hub-prod/test_notebooks2.py +0 -0
  71. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/index.md +0 -0
  72. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/notebooks.md +0 -0
  73. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/docs/reference.md +0 -0
  74. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_cache.py +0 -0
  75. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_check.py +0 -0
  76. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_django.py +0 -0
  77. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_entry_points.py +0 -0
  78. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_register_instance.py +0 -0
  79. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_schema.py +0 -0
  80. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/_set_managed_storage.py +0 -0
  81. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/__init__.py +0 -0
  82. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_aws_storage.py +0 -0
  83. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_deprecated.py +0 -0
  84. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_docs.py +0 -0
  85. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_hub_crud.py +0 -0
  86. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_hub_utils.py +0 -0
  87. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_save.py +0 -0
  88. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_store.py +0 -0
  89. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_user.py +0 -0
  90. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/_setup_bionty_sources.py +0 -0
  91. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/cloud_sqlite_locker.py +0 -0
  92. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/exceptions.py +0 -0
  93. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/hashing.py +0 -0
  94. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/core/types.py +0 -0
  95. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/py.typed +0 -0
  96. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/lamindb_setup/types.py +0 -0
  97. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/scripts/script-init-pass-user-no-writes.py +0 -0
  98. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/scripts/script-to-fail-managed-storage.py +0 -0
  99. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/test_delete_instance.py +0 -0
  100. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/test_fail_managed_storage.py +0 -0
  101. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/test_init_pass_user_no_writes.py +0 -0
  102. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-cloud/test_set_storage.py +0 -0
  103. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-local/README.md +0 -0
  104. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-local/scripts/script-connect-fine-grained-access.py +0 -0
  105. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-prod/conftest.py +0 -0
  106. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-prod/test_aws_options_manager.py +0 -0
  107. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-prod/test_django.py +0 -0
  108. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-prod/test_global_settings.py +0 -0
  109. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-prod/test_migrate.py +0 -0
  110. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/hub-prod/test_upath.py +0 -0
  111. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/storage/conftest.py +0 -0
  112. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/storage/test_entry_point.py +0 -0
  113. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/storage/test_hashing.py +0 -0
  114. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/storage/test_storage_settings.py +0 -0
  115. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/storage/test_storage_stats.py +0 -0
  116. {lamindb_setup-1.15.2 → lamindb_setup-1.17.0}/tests/storage/test_to_url.py +0 -0
@@ -12,7 +12,7 @@ jobs:
12
12
  # tests only on production hub
13
13
  hub-prod:
14
14
  runs-on: ubuntu-latest
15
- timeout-minutes: 12
15
+ timeout-minutes: 13
16
16
  steps:
17
17
  - uses: actions/checkout@v4
18
18
  - uses: actions/setup-python@v6
@@ -44,7 +44,7 @@ jobs:
44
44
  python-version: "3.11"
45
45
  - lamin_env: "staging"
46
46
  python-version: "3.10"
47
- timeout-minutes: 12
47
+ timeout-minutes: 13
48
48
  steps:
49
49
  - uses: aws-actions/configure-aws-credentials@v4
50
50
  with:
@@ -94,7 +94,7 @@ jobs:
94
94
  # test user access to storage
95
95
  storage:
96
96
  runs-on: ubuntu-latest
97
- timeout-minutes: 12
97
+ timeout-minutes: 13
98
98
  steps:
99
99
  - uses: actions/checkout@v4
100
100
  - uses: actions/setup-python@v6
@@ -107,7 +107,6 @@ jobs:
107
107
  - run: nox -s lint
108
108
  - run: nox -s storage
109
109
  env:
110
- TEST_INSTANCE_PRIVATE_POSTGRES: ${{ secrets.TEST_INSTANCE_PRIVATE_POSTGRES }}
111
110
  TMP_AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
112
111
  TMP_AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
113
112
  - uses: actions/upload-artifact@v4
@@ -119,7 +118,7 @@ jobs:
119
118
  # test low-level hub functionality
120
119
  hub-local:
121
120
  runs-on: ubuntu-latest
122
- timeout-minutes: 12
121
+ timeout-minutes: 13
123
122
  steps:
124
123
  - uses: aws-actions/configure-aws-credentials@v4
125
124
  with:
@@ -163,6 +162,60 @@ jobs:
163
162
  path: .coverage
164
163
  include-hidden-files: true
165
164
 
165
+ # test custom proxies and certificates
166
+ connectivity:
167
+ runs-on: ubuntu-latest
168
+ timeout-minutes: 13
169
+ steps:
170
+ - uses: aws-actions/configure-aws-credentials@v4
171
+ with:
172
+ aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
173
+ aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
174
+ aws-region: eu-central-1
175
+ - uses: actions/checkout@v4
176
+ - uses: actions/setup-python@v6
177
+ with:
178
+ python-version: "3.11"
179
+ cache: "pip"
180
+ cache-dependency-path: ".github/workflows/build.yml"
181
+ - run: pip install "laminci@git+https://x-access-token:${{ secrets.LAMIN_BUILD_DOCS }}@github.com/laminlabs/laminci"
182
+ - run: nox -s "install(group='connectivity')"
183
+ - name: cache mitmproxy
184
+ id: cache-mitmproxy
185
+ uses: actions/cache@v4
186
+ with:
187
+ path: ~/mitmproxy.tar
188
+ key: cache-mitmproxy-0
189
+ - name: cache mitmproxy miss
190
+ if: ${{ steps.cache-mitmproxy.outputs.cache-hit != 'true' }}
191
+ run: docker pull mitmproxy/mitmproxy:latest && docker image save mitmproxy/mitmproxy:latest --output ~/mitmproxy.tar
192
+ - name: cache mitmproxy use
193
+ if: ${{ steps.cache-mitmproxy.outputs.cache-hit == 'true' }}
194
+ run: docker image load --input ~/mitmproxy.tar
195
+ - name: start mitmproxy
196
+ run: |
197
+ # Start mitmdump (headless mitmproxy) as a forward proxy on 8080
198
+ docker run -d \
199
+ --name mitmproxy \
200
+ -p 8080:8080 \
201
+ mitmproxy/mitmproxy:latest \
202
+ mitmdump --mode regular --listen-port 8080
203
+ # Give it a few seconds to start and generate the CA
204
+ sleep 10
205
+ echo "mitmproxy state:"
206
+ docker ps -a
207
+ echo "Container ~/.mitmproxy contents:"
208
+ docker exec mitmproxy ls -l /home/mitmproxy/.mitmproxy || true
209
+ echo "Container logs (if it still exited):"
210
+ docker logs mitmproxy || true
211
+ - name: export mitmproxy certificate
212
+ run: |
213
+ # Copy CA cert from inside the container to the workspace
214
+ docker cp mitmproxy:/home/mitmproxy/.mitmproxy/mitmproxy-ca-cert.pem ./mitmproxy-ca.pem
215
+ ls -l ./mitmproxy-ca.pem
216
+ - name: run tests
217
+ run: nox -s connectivity
218
+
166
219
  coverage:
167
220
  needs: [hub-prod, hub-cloud, storage, hub-local]
168
221
  runs-on: ubuntu-latest
@@ -115,3 +115,4 @@ storage_uid.txt
115
115
  test.ipynb
116
116
  test2.ipynb
117
117
  *_export
118
+ update_clone.py
@@ -1,22 +1,22 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: lamindb_setup
3
- Version: 1.15.2
3
+ Version: 1.17.0
4
4
  Summary: Setup & configure LaminDB.
5
5
  Author-email: Lamin Labs <open-source@lamin.ai>
6
6
  Requires-Python: >=3.10
7
7
  Description-Content-Type: text/markdown
8
+ License-File: LICENSE
8
9
  Requires-Dist: lamin_utils>=0.3.3
9
10
  Requires-Dist: django>=5.2,<5.3
10
11
  Requires-Dist: dj_database_url>=1.3.0,<3.0.0
12
+ Requires-Dist: django-pgtrigger
11
13
  Requires-Dist: pydantic-settings
12
14
  Requires-Dist: platformdirs<5.0.0
13
15
  Requires-Dist: httpx_retries<1.0.0
14
16
  Requires-Dist: requests
15
17
  Requires-Dist: universal_pathlib==0.2.6
16
18
  Requires-Dist: botocore<2.0.0
17
- Requires-Dist: supabase>=2.8.1,<=2.16.0
18
- Requires-Dist: gotrue<=2.12.0
19
- Requires-Dist: storage3!=0.11.2; python_version < '3.11'
19
+ Requires-Dist: supabase>=2.20.0,<=2.24.0
20
20
  Requires-Dist: pyjwt<3.0.0
21
21
  Requires-Dist: psutil
22
22
  Requires-Dist: packaging
@@ -35,14 +35,17 @@ Migration management
35
35
 
36
36
  """
37
37
 
38
- __version__ = "1.15.2" # denote a release candidate for 0.1.0 with 0.1rc1
38
+ __version__ = "1.17.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
@@ -0,0 +1,131 @@
1
+ from __future__ import annotations
2
+
3
+ import functools
4
+ import importlib as il
5
+ import inspect
6
+ import os
7
+ from typing import TYPE_CHECKING
8
+ from uuid import UUID
9
+
10
+ from lamin_utils import logger
11
+
12
+ from ._silence_loggers import silence_loggers
13
+ from .core import django as django_lamin
14
+ from .core._settings import settings
15
+ from .core._settings_store import current_instance_settings_file
16
+ from .errors import (
17
+ MODULE_WASNT_CONFIGURED_MESSAGE_TEMPLATE,
18
+ ModuleWasntConfigured,
19
+ )
20
+
21
+ if TYPE_CHECKING:
22
+ from collections.abc import Callable
23
+
24
+ from .core._settings_instance import InstanceSettings
25
+
26
+
27
+ IS_LOADING: bool = False
28
+
29
+
30
+ # decorator to disable auto-connect when importing a module such as lamindb
31
+ def disable_auto_connect(func: Callable):
32
+ @functools.wraps(func)
33
+ def wrapper(*args, **kwargs):
34
+ global IS_LOADING
35
+ IS_LOADING = True
36
+ try:
37
+ return func(*args, **kwargs)
38
+ finally:
39
+ IS_LOADING = False
40
+
41
+ return wrapper
42
+
43
+
44
+ def _normalize_module_name(module_name: str) -> str:
45
+ return module_name.replace("lnschema_", "").replace("_", "-")
46
+
47
+
48
+ # checks that the provided modules is in the modules of the provided instance
49
+ # or in the apps setup by django
50
+ def _check_module_in_instance_modules(
51
+ module: str, isettings: InstanceSettings | None = None
52
+ ) -> None:
53
+ if isettings is not None:
54
+ modules_raw = isettings.modules
55
+ modules = set(modules_raw).union(
56
+ _normalize_module_name(module) for module in modules_raw
57
+ )
58
+ if _normalize_module_name(module) not in modules and module not in modules:
59
+ raise ModuleWasntConfigured(
60
+ MODULE_WASNT_CONFIGURED_MESSAGE_TEMPLATE.format(module)
61
+ )
62
+ else:
63
+ return
64
+
65
+ from django.apps import apps
66
+
67
+ for app in apps.get_app_configs():
68
+ # app.name is always unnormalized module (python package) name
69
+ if module == app.name or module == _normalize_module_name(app.name):
70
+ return
71
+ raise ModuleWasntConfigured(MODULE_WASNT_CONFIGURED_MESSAGE_TEMPLATE.format(module))
72
+
73
+
74
+ # infer the name of the module that calls this function
75
+ def _infer_callers_module_name() -> str | None:
76
+ stack = inspect.stack()
77
+ if len(stack) < 3:
78
+ return None
79
+ module = inspect.getmodule(stack[2][0])
80
+ return module.__name__.partition(".")[0] if module is not None else None
81
+
82
+
83
+ # we make this a private function because in all the places it's used,
84
+ # users should not see it
85
+ def _check_instance_setup(from_module: str | None = None) -> bool:
86
+ if django_lamin.IS_SETUP:
87
+ if from_module is not None:
88
+ if from_module != "lamindb":
89
+ _check_module_in_instance_modules(from_module)
90
+ else:
91
+ infer_module = _infer_callers_module_name()
92
+ if infer_module is not None and infer_module not in {
93
+ "lamindb",
94
+ "lamindb_setup",
95
+ "lamin_cli",
96
+ }:
97
+ _check_module_in_instance_modules(infer_module)
98
+ return True
99
+ silence_loggers()
100
+ if os.environ.get("LAMINDB_MULTI_INSTANCE") == "true":
101
+ logger.warning(
102
+ "running LaminDB in multi-instance mode; you'll experience "
103
+ "errors in regular lamindb usage"
104
+ )
105
+ return True
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)
117
+ return django_lamin.IS_SETUP
118
+ else:
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
@@ -266,13 +263,8 @@ def validate_connection_state(
266
263
  from django.db import connection
267
264
 
268
265
  if (
269
- settings._instance_exists
266
+ settings._instance_exists # exists only for real instances, not for none/none
270
267
  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
268
  and not use_root_db_user # always re-connect for root db user
277
269
  ):
278
270
  logger.important(
@@ -280,7 +272,7 @@ def validate_connection_state(
280
272
  )
281
273
  return None
282
274
  else:
283
- if settings._instance_exists and settings.instance.slug != "none/none":
275
+ if settings._instance_exists:
284
276
  import lamindb as ln
285
277
 
286
278
  if ln.context.transform is not None:
@@ -292,7 +284,9 @@ def validate_connection_state(
292
284
 
293
285
  @unlock_cloud_sqlite_upon_exception(ignore_prev_locker=True)
294
286
  def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
295
- """Connect to an instance.
287
+ """Connect the global default instance.
288
+
289
+ If you want to create a read-only database client, use :class:`~lamindb.DB` instead.
296
290
 
297
291
  Args:
298
292
  instance: Pass a slug (`account/name`) or URL (`https://lamin.ai/account/name`).
@@ -340,12 +334,9 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
340
334
  if settings._instance_exists:
341
335
  isettings = settings.instance
342
336
  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
337
+ raise ValueError(
338
+ "No instance was connected through the CLI, pass a value to `instance` or connect via the CLI."
339
+ )
349
340
  if use_root_db_user:
350
341
  reset_django()
351
342
  owner, name = isettings.owner, isettings.name
@@ -414,7 +405,6 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
414
405
 
415
406
  load_from_isettings(isettings, user=_user, write_settings=_write_settings)
416
407
  if _reload_lamindb:
417
- importlib.reload(importlib.import_module("lamindb"))
418
408
  reset_django_module_variables()
419
409
  if isettings.slug != "none/none":
420
410
  logger.important(f"connected lamindb: {isettings.slug}")
@@ -424,10 +414,6 @@ def connect(instance: str | None = None, **kwargs: Any) -> str | tuple | None:
424
414
  isettings._get_settings_file().unlink(missing_ok=True) # type: ignore
425
415
  settings._instance_settings = None
426
416
  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
417
  return None
432
418
 
433
419
 
@@ -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:
@@ -125,24 +125,10 @@ class migrate:
125
125
  from lamindb_setup._schema_metadata import update_schema_in_hub
126
126
  from lamindb_setup.core._hub_client import call_with_fallback_auth
127
127
  from lamindb_setup.core._hub_crud import (
128
- select_collaborator,
129
128
  update_instance,
130
129
  )
131
130
 
132
131
  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
- )
146
132
  # ensure we connect with the root user
147
133
  if "root" not in settings.instance.db:
148
134
  connect(use_root_db_user=True)
@@ -172,7 +172,8 @@ class _ModelHandler:
172
172
  self.table_name = model._meta.db_table
173
173
  self.included_modules = included_modules
174
174
  self.fields = self._get_fields_metadata(self.model)
175
- self.is_link_table = issubclass(model, IsLink)
175
+ self.is_auto_created = bool(model._meta.auto_created)
176
+ self.is_link_table = issubclass(model, IsLink) or self.is_auto_created
176
177
  self.name_field = model._name_field if hasattr(model, "_name_field") else None
177
178
  self.ontology_id_field = (
178
179
  model._ontology_id_field if hasattr(model, "_ontology_id_field") else None
@@ -183,6 +184,7 @@ class _ModelHandler:
183
184
  "fields": self.fields.copy(),
184
185
  "class_name": self.class_name,
185
186
  "table_name": self.table_name,
187
+ "is_auto_created": self.is_auto_created,
186
188
  "is_link_table": self.is_link_table,
187
189
  "name_field": self.name_field,
188
190
  "ontology_id_field": self.ontology_id_field,
@@ -249,13 +251,13 @@ class _ModelHandler:
249
251
  return related_fields
250
252
 
251
253
  def _get_field_metadata(self, model, field: Field):
252
- from lamindb.models import IsLink
254
+ from lamindb.models import IsLink, Registry
253
255
 
254
256
  internal_type = field.get_internal_type()
255
257
  model_name = field.model._meta.model_name
256
258
  relation_type = self._get_relation_type(model, field)
257
259
 
258
- schema_name = field.model.__get_module_name__()
260
+ schema_name = Registry.__get_module_name__(field.model)
259
261
 
260
262
  if field.related_model is None:
261
263
  related_model_name = None
@@ -265,7 +267,7 @@ class _ModelHandler:
265
267
  max_length = field.max_length
266
268
  else:
267
269
  related_model_name = field.related_model._meta.model_name
268
- related_schema_name = field.related_model.__get_module_name__()
270
+ related_schema_name = Registry.__get_module_name__(field.related_model)
269
271
  related_field_name = field.remote_field.name
270
272
  is_editable = False
271
273
  max_length = None
@@ -418,14 +420,10 @@ class _SchemaHandler:
418
420
  all_models = {module_name: {} for module_name in self.included_modules}
419
421
 
420
422
  # Iterate through all registered Django models
421
- for model in apps.get_models():
423
+ for model in apps.get_models(include_auto_created=True):
422
424
  # Check if model meets the criteria
423
- if (
424
- model.__class__ is Registry
425
- and model is not SQLRecord
426
- and not model._meta.abstract
427
- ):
428
- module_name = model.__get_module_name__()
425
+ if model is not SQLRecord and not model._meta.abstract:
426
+ module_name = Registry.__get_module_name__(model)
429
427
  # Only include if module is in our included list
430
428
  if module_name in self.included_modules:
431
429
  model_name = model._meta.model_name
@@ -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
@@ -43,6 +44,14 @@ def load_user(email: str | None = None, handle: str | None = None) -> UserSettin
43
44
  return user_settings
44
45
 
45
46
 
47
+ def current_user_uid() -> str:
48
+ current_user_settings = current_user_settings_file()
49
+ if current_user_settings.exists():
50
+ return load_user_settings(current_user_settings).uid
51
+
52
+ return "00000000" # anonymous
53
+
54
+
46
55
  def login(
47
56
  user: str | None = None, *, api_key: str | None = None, **kwargs
48
57
  ) -> UserSettings:
@@ -51,15 +60,14 @@ def login(
51
60
 
52
61
  `login()` prompts for your API key unless you set it via the `LAMIN_API_KEY` environment variable or pass it as an argument.
53
62
 
54
- 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>`__.
55
66
 
56
67
  Args:
57
68
  user: User handle.
58
69
  api_key: API key.
59
70
 
60
- See Also:
61
- Login via the CLI command `lamin login`, see `here <https://docs.lamin.ai/cli#login>`__.
62
-
63
71
  Examples:
64
72
 
65
73
  Logging in the first time::
@@ -72,12 +80,15 @@ def login(
72
80
 
73
81
  ln.setup.login("myhandle") # pass your user handle
74
82
  """
83
+ from getpass import getpass
84
+
75
85
  if user is None:
76
86
  if api_key is None:
77
87
  if "LAMIN_API_KEY" in os.environ:
78
88
  api_key = os.environ["LAMIN_API_KEY"]
79
89
  else:
80
- 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: ")
81
92
  elif api_key is not None:
82
93
  raise ValueError("Please provide either 'user' or 'api_key', not both.")
83
94
 
@@ -90,6 +101,9 @@ def login(
90
101
  "the legacy API key is deprecated and will likely be removed in a future version"
91
102
  )
92
103
 
104
+ # do this here because load_user overwrites current_user_settings_file
105
+ previous_user_uid = current_user_uid()
106
+
93
107
  if api_key is None:
94
108
  if "@" in user: # type: ignore
95
109
  email, handle = user, None
@@ -144,8 +158,15 @@ def login(
144
158
  user_settings.api_key = api_key
145
159
  save_user_settings(user_settings)
146
160
 
147
- if settings._instance_exists and _check_instance_setup():
148
- register_user(user_settings)
161
+ if settings._instance_exists:
162
+ if (
163
+ isettings := settings.instance
164
+ ).is_on_hub and previous_user_uid != user_settings.uid:
165
+ logger.important_hint(
166
+ f"consider re-connecting to update permissions: lamin connect {isettings.slug}"
167
+ )
168
+ if _check_instance_setup():
169
+ register_user(user_settings)
149
170
 
150
171
  settings._user_settings = None
151
172
  # aws s3 credentials are scoped to the user
@@ -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