lamindb_setup 1.16.0__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.16.0 → lamindb_setup-1.17.0}/.github/workflows/build.yml +54 -1
  2. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/.gitignore +1 -0
  3. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/PKG-INFO +4 -5
  4. {lamindb_setup-1.16.0 → 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.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_connect_instance.py +9 -23
  7. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_delete.py +10 -5
  8. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_disconnect.py +12 -9
  9. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_init_instance.py +0 -1
  10. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_migrate.py +0 -14
  11. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_setup_user.py +8 -5
  12. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_silence_loggers.py +2 -0
  13. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_aws_options.py +17 -7
  14. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_hub_client.py +1 -2
  15. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_hub_core.py +9 -6
  16. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings.py +14 -10
  17. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_instance.py +28 -6
  18. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_load.py +25 -7
  19. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/django.py +46 -18
  20. lamindb_setup-1.17.0/lamindb_setup/core/lamin.db.gz +0 -0
  21. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/upath.py +15 -6
  22. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/errors.py +0 -12
  23. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/io.py +16 -5
  24. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/noxfile.py +18 -3
  25. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/pyproject.toml +1 -3
  26. lamindb_setup-1.17.0/tests/connectivity/conftest.py +33 -0
  27. lamindb_setup-1.17.0/tests/connectivity/test_proxies_certificates.py +58 -0
  28. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/test_clone_instance.py +1 -1
  29. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/test_connect_instance.py +15 -6
  30. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/test_edge_request.py +1 -1
  31. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/test_init_instance.py +1 -0
  32. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/test_login.py +3 -8
  33. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-local/conftest.py +8 -2
  34. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-local/test_all.py +2 -2
  35. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-prod/test_switch_and_fallback_env.py +2 -2
  36. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/storage/test_db_import_export.py +2 -2
  37. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/storage/test_storage_access.py +4 -8
  38. lamindb_setup-1.16.0/docs/hub-prod/test-insufficient-user-info.ipynb +0 -192
  39. lamindb_setup-1.16.0/lamindb_setup/_check_setup.py +0 -192
  40. lamindb_setup-1.16.0/tests/storage/test_httpx_client.py +0 -21
  41. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/.github/workflows/doc-changes.yml +0 -0
  42. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/.pre-commit-config.yaml +0 -0
  43. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/LICENSE +0 -0
  44. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/README.md +0 -0
  45. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/changelog.md +0 -0
  46. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-cloud/01-init-local-instance.ipynb +0 -0
  47. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-cloud/02-connect-local-instance.ipynb +0 -0
  48. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-cloud/03-add-managed-storage.ipynb +0 -0
  49. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-cloud/04-test-bionty.ipynb +0 -0
  50. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-cloud/05-init-hosted-instance.ipynb +0 -0
  51. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-cloud/06-connect-hosted-instance.ipynb +0 -0
  52. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-cloud/07-keep-artifacts-local.ipynb +0 -0
  53. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-cloud/08-test-multi-session.ipynb +0 -0
  54. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-cloud/09-test-migrate.ipynb +0 -0
  55. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-cloud/test_notebooks.py +0 -0
  56. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-prod/test-cache-management.ipynb +0 -0
  57. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-prod/test-cloud-sync.ipynb +0 -0
  58. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-prod/test-connect-anonymously.ipynb +0 -0
  59. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-prod/test-empty-init.ipynb +0 -0
  60. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-prod/test-import-schema.ipynb +0 -0
  61. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-prod/test-init-load-local-anonymously.ipynb +0 -0
  62. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-prod/test-invalid-schema.ipynb +0 -0
  63. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-prod/test-sqlite-lock.ipynb +0 -0
  64. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/hub-prod/test_notebooks2.py +0 -0
  65. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/index.md +0 -0
  66. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/notebooks.md +0 -0
  67. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/docs/reference.md +0 -0
  68. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_cache.py +0 -0
  69. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_check.py +0 -0
  70. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_django.py +0 -0
  71. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_entry_points.py +0 -0
  72. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_register_instance.py +0 -0
  73. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_schema.py +0 -0
  74. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_schema_metadata.py +0 -0
  75. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/_set_managed_storage.py +0 -0
  76. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/__init__.py +0 -0
  77. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_aws_storage.py +0 -0
  78. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_clone.py +0 -0
  79. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_deprecated.py +0 -0
  80. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_docs.py +0 -0
  81. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_hub_crud.py +0 -0
  82. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_hub_utils.py +0 -0
  83. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_private_django_api.py +0 -0
  84. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_save.py +0 -0
  85. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_storage.py +0 -0
  86. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_store.py +0 -0
  87. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_settings_user.py +0 -0
  88. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/_setup_bionty_sources.py +0 -0
  89. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/cloud_sqlite_locker.py +0 -0
  90. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/exceptions.py +0 -0
  91. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/hashing.py +0 -0
  92. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/core/types.py +0 -0
  93. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/py.typed +0 -0
  94. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/lamindb_setup/types.py +0 -0
  95. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/scripts/script-init-pass-user-no-writes.py +0 -0
  96. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/scripts/script-to-fail-managed-storage.py +0 -0
  97. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/test_delete_instance.py +0 -0
  98. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/test_fail_managed_storage.py +0 -0
  99. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/test_init_pass_user_no_writes.py +0 -0
  100. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-cloud/test_set_storage.py +0 -0
  101. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-local/README.md +0 -0
  102. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-local/scripts/script-connect-fine-grained-access.py +0 -0
  103. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-local/test_update_schema_in_hub.py +0 -0
  104. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-prod/conftest.py +0 -0
  105. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-prod/test_aws_options_manager.py +0 -0
  106. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-prod/test_django.py +0 -0
  107. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-prod/test_global_settings.py +0 -0
  108. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-prod/test_migrate.py +0 -0
  109. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/hub-prod/test_upath.py +0 -0
  110. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/storage/conftest.py +0 -0
  111. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/storage/test_entry_point.py +0 -0
  112. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/storage/test_hashing.py +0 -0
  113. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/storage/test_storage_basis.py +0 -0
  114. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/storage/test_storage_settings.py +0 -0
  115. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/storage/test_storage_stats.py +0 -0
  116. {lamindb_setup-1.16.0 → lamindb_setup-1.17.0}/tests/storage/test_to_url.py +0 -0
@@ -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
@@ -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,10 +1,11 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: lamindb_setup
3
- Version: 1.16.0
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
@@ -15,9 +16,7 @@ Requires-Dist: httpx_retries<1.0.0
15
16
  Requires-Dist: requests
16
17
  Requires-Dist: universal_pathlib==0.2.6
17
18
  Requires-Dist: botocore<2.0.0
18
- Requires-Dist: supabase>=2.8.1,<=2.16.0
19
- Requires-Dist: gotrue<=2.12.0
20
- Requires-Dist: storage3!=0.11.2; python_version < '3.11'
19
+ Requires-Dist: supabase>=2.20.0,<=2.24.0
21
20
  Requires-Dist: pyjwt<3.0.0
22
21
  Requires-Dist: psutil
23
22
  Requires-Dist: packaging
@@ -35,14 +35,17 @@ Migration management
35
35
 
36
36
  """
37
37
 
38
- __version__ = "1.16.0" # denote a release candidate for 0.1.0 with 0.1rc1
38
+ __version__ = "1.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)
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ from time import sleep
4
5
  from typing import TYPE_CHECKING
5
6
 
6
7
  from lamin_utils import logger
@@ -59,15 +60,14 @@ def login(
59
60
 
60
61
  `login()` prompts for your API key unless you set it via the `LAMIN_API_KEY` environment variable or pass it as an argument.
61
62
 
62
- You can create your API key in your account settings on LaminHub (top right corner).
63
+ You can create your API key in your account settings on LaminHub at `lamin.ai/settings <https://lamin.ai/settings>`__.
64
+
65
+ Note that the preferred method is to use the CLI command `lamin login`: `docs.lamin.ai/cli#login <https://docs.lamin.ai/cli#login>`__.
63
66
 
64
67
  Args:
65
68
  user: User handle.
66
69
  api_key: API key.
67
70
 
68
- See Also:
69
- Login via the CLI command `lamin login`, see `here <https://docs.lamin.ai/cli#login>`__.
70
-
71
71
  Examples:
72
72
 
73
73
  Logging in the first time::
@@ -80,12 +80,15 @@ def login(
80
80
 
81
81
  ln.setup.login("myhandle") # pass your user handle
82
82
  """
83
+ from getpass import getpass
84
+
83
85
  if user is None:
84
86
  if api_key is None:
85
87
  if "LAMIN_API_KEY" in os.environ:
86
88
  api_key = os.environ["LAMIN_API_KEY"]
87
89
  else:
88
- api_key = input("Your API key: ")
90
+ print("Copy your API key. To create one: https://lamin.ai/settings")
91
+ api_key = getpass("Paste it: ")
89
92
  elif api_key is not None:
90
93
  raise ValueError("Please provide either 'user' or 'api_key', not both.")
91
94
 
@@ -25,6 +25,8 @@ def silence_loggers():
25
25
  set_stream_logger(name="botocore.auth", level=logging.WARNING)
26
26
  set_stream_logger(name="botocore.endpoint", level=logging.WARNING)
27
27
  set_stream_logger(name="httpx", level=logging.WARNING)
28
+ set_stream_logger(name="httpcore", level=logging.WARNING)
29
+ set_stream_logger(name="hpack", level=logging.WARNING)
28
30
  try:
29
31
  import aiobotocore
30
32
 
@@ -60,6 +60,7 @@ class AWSOptionsManager:
60
60
  from aiobotocore.session import AioSession
61
61
  from s3fs import S3FileSystem
62
62
 
63
+ anon_env = os.getenv("LAMIN_S3_ANON") == "true"
63
64
  # this is cached so will be resued with the connection initialized
64
65
  # these options are set for paths in _path_inject_options
65
66
  # here we set the same options to cache the filesystem
@@ -68,19 +69,28 @@ class AWSOptionsManager:
68
69
  use_listings_cache=True,
69
70
  version_aware=False,
70
71
  config_kwargs={"max_pool_connections": 64},
72
+ anon=anon_env,
71
73
  )
72
74
 
73
75
  self._suppress_aiobotocore_traceback_logging()
74
76
 
75
- try:
76
- fs.connect()
77
- self.anon: bool = fs.session._credentials is None
78
- except Exception as e:
77
+ if anon_env:
78
+ self.anon: bool = True
79
79
  logger.warning(
80
- f"There is a problem with your default AWS Credentials: {e}\n"
81
- "`anon` mode will be used for all non-managed buckets."
80
+ "`anon` mode will be used for all non-managed buckets "
81
+ "because the environment variable LAMIN_S3_ANON was set to 'true'"
82
82
  )
83
- self.anon = True
83
+ else:
84
+ try:
85
+ fs.connect()
86
+ self.anon = fs.session._credentials is None
87
+ except Exception as e:
88
+ logger.warning(
89
+ f"There is a problem with your default AWS Credentials: {e}\n"
90
+ "`anon` mode will be used for all non-managed buckets"
91
+ )
92
+ self.anon = True
93
+
84
94
  self.anon_public: bool | None = None
85
95
  if not self.anon:
86
96
  try:
@@ -11,8 +11,7 @@ import httpx
11
11
  from httpx_retries import Retry, RetryTransport
12
12
  from lamin_utils import logger
13
13
  from pydantic_settings import BaseSettings
14
- from supabase import Client, create_client # type: ignore
15
- from supabase.lib.client_options import ClientOptions
14
+ from supabase import Client, ClientOptions, create_client
16
15
 
17
16
  from ._settings_save import save_user_settings
18
17
 
@@ -383,7 +383,8 @@ def _init_instance_hub(
383
383
  ) -> None:
384
384
  from ._settings import settings
385
385
 
386
- account_id = settings.user._uuid if account_id is None else account_id
386
+ created_by_id = settings.user._uuid.hex if account_id is None else account_id.hex # type: ignore
387
+ owner_account_id = os.getenv("LAMINDB_ACCOUNT_ID_INIT", created_by_id)
387
388
 
388
389
  try:
389
390
  lamindb_version = metadata.version("lamindb")
@@ -391,13 +392,13 @@ def _init_instance_hub(
391
392
  lamindb_version = None
392
393
  fields = {
393
394
  "id": isettings._id.hex,
394
- "account_id": account_id.hex, # type: ignore
395
+ "account_id": owner_account_id,
395
396
  "name": isettings.name,
396
397
  "lnid": isettings.uid,
397
398
  "schema_str": isettings._schema_str,
398
399
  "lamindb_version": lamindb_version,
399
400
  "public": False,
400
- "created_by_id": account_id.hex, # type: ignore
401
+ "created_by_id": created_by_id,
401
402
  }
402
403
  if isettings.dialect != "sqlite":
403
404
  db_dsn = LaminDsnModel(db=isettings.db)
@@ -407,7 +408,7 @@ def _init_instance_hub(
407
408
  "db_port": db_dsn.db.port,
408
409
  "db_database": db_dsn.db.database,
409
410
  }
410
- fields.update(db_fields)
411
+ fields.update(db_fields) # type: ignore
411
412
  slug = isettings.slug
412
413
  # I'd like the following to be an upsert, but this seems to violate RLS
413
414
  # Similarly, if we don't specify `returning="minimal"`, we'll violate RLS
@@ -415,7 +416,9 @@ def _init_instance_hub(
415
416
  # as then init_instance is no longer idempotent
416
417
  try:
417
418
  client.table("instance").insert(fields, returning="minimal").execute()
418
- except APIError:
419
+ except APIError as e:
420
+ if "new row violates row-level security policy" in str(e):
421
+ raise e
419
422
  logger.warning(f"instance already existed at: https://lamin.ai/{slug}")
420
423
  return None
421
424
  if isettings.dialect != "sqlite" and isettings.is_remote:
@@ -713,7 +716,7 @@ def get_lamin_site_base_url():
713
716
 
714
717
 
715
718
  def sign_up_local_hub(email) -> str | tuple[str, str, str]:
716
- # raises gotrue.errors.AuthApiError: User already registered
719
+ # raises AuthApiError: User already registered
717
720
  password = base62(40) # generate new password
718
721
  sign_up_kwargs = {"email": email, "password": password}
719
722
  client = connect_hub()
@@ -46,6 +46,12 @@ def _process_cache_path(cache_path: UPathStr | None) -> UPath | None:
46
46
  return cache_dir
47
47
 
48
48
 
49
+ # returned by settings.branch for none/none instance
50
+ class MainBranchMock:
51
+ id = 1
52
+ name = "main"
53
+
54
+
49
55
  class SetupSettings:
50
56
  """Setup settings."""
51
57
 
@@ -140,6 +146,10 @@ class SetupSettings:
140
146
  # and we never need a DB request
141
147
  def branch(self) -> Branch:
142
148
  """Default branch."""
149
+ # this is needed for .filter() with non-default connections
150
+ if not self._instance_exists:
151
+ return MainBranchMock()
152
+
143
153
  if self._branch is None:
144
154
  from lamindb import Branch
145
155
 
@@ -222,10 +232,9 @@ class SetupSettings:
222
232
  If `True`, the current instance is connected, meaning that the db and other settings
223
233
  are properly configured for use.
224
234
  """
225
- if self._instance_exists:
226
- return self.instance.slug != "none/none"
227
- else:
228
- return False
235
+ from . import django
236
+
237
+ return self._instance_exists and django.IS_SETUP
229
238
 
230
239
  @property
231
240
  def private_django_api(self) -> bool:
@@ -284,12 +293,7 @@ class SetupSettings:
284
293
 
285
294
  @property
286
295
  def _instance_exists(self):
287
- try:
288
- self.instance # noqa
289
- return True
290
- # this is implicit logic that catches if no instance is loaded
291
- except CurrentInstanceNotConfigured:
292
- return False
296
+ return self.instance.slug != "none/none"
293
297
 
294
298
  @property
295
299
  def cache_dir(self) -> UPath: