lamindb_setup 1.0.3__tar.gz → 1.1.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.
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/PKG-INFO +6 -4
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-cloud/01-init-local-instance.ipynb +1 -2
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-cloud/02-connect-local-instance.ipynb +50 -3
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-cloud/05-init-hosted-instance.ipynb +1 -1
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-prod/test-cloud-sync.ipynb +22 -6
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/__init__.py +17 -1
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_check_setup.py +68 -9
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_connect_instance.py +9 -1
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_delete.py +1 -1
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_init_instance.py +1 -6
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_migrate.py +6 -6
- lamindb_setup-1.0.3/lamindb_setup/core/_aws_credentials.py → lamindb_setup-1.1.1/lamindb_setup/core/_aws_options.py +39 -18
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_hub_client.py +5 -1
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_hub_core.py +4 -4
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_private_django_api.py +0 -5
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_settings_instance.py +11 -14
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_settings_storage.py +33 -16
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/hashing.py +13 -6
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/upath.py +66 -9
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/noxfile.py +3 -2
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/pyproject.toml +6 -4
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/test_init_instance.py +23 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-local/conftest.py +2 -2
- lamindb_setup-1.1.1/tests/hub-prod/test_aws_options_manager.py +18 -0
- lamindb_setup-1.1.1/tests/hub-prod/test_upath.py +70 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/storage/test_hashing.py +6 -0
- lamindb_setup-1.1.1/tests/storage/test_storage_settings.py +24 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/storage/test_storage_stats.py +3 -1
- lamindb_setup-1.0.3/tests/hub-prod/test_aws_credentials_manager.py +0 -20
- lamindb_setup-1.0.3/tests/hub-prod/test_upath.py +0 -44
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/.github/workflows/build.yml +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/.github/workflows/doc-changes.yml +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/.gitignore +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/.pre-commit-config.yaml +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/LICENSE +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/README.md +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/changelog.md +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-cloud/03-add-managed-storage.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-cloud/04-test-bionty.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-cloud/06-connect-hosted-instance.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-cloud/07-keep-artifacts-local.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-cloud/08-test-multi-session.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-cloud/test_notebooks.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-prod/test-cache-management.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-prod/test-connect-anonymously.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-prod/test-empty-init.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-prod/test-import-schema.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-prod/test-init-load-local-anonymously.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-prod/test-insufficient-user-info.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-prod/test-invalid-schema.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-prod/test-sqlite-lock.ipynb +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/hub-prod/test_notebooks2.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/index.md +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/notebooks.md +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/docs/reference.md +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_cache.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_check.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_close.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_django.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_entry_points.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_exportdb.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_importdb.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_register_instance.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_schema.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_schema_metadata.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_set_managed_storage.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_setup_user.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/_silence_loggers.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/__init__.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_aws_storage.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_deprecated.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_docs.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_hub_crud.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_hub_utils.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_settings.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_settings_load.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_settings_save.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_settings_store.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_settings_user.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/_setup_bionty_sources.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/cloud_sqlite_locker.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/django.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/exceptions.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/lamindb_setup/core/types.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/scripts/script-init-pass-user-no-writes.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/scripts/script-to-fail-managed-storage.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/test_connect_instance.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/test_delete_instance.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/test_edge_request.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/test_fail_managed_storage.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/test_init_pass_user_no_writes.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/test_login.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/test_migrate.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-cloud/test_set_storage.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-local/test_all.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-local/test_update_schema_in_hub.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-prod/conftest.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-prod/test_django.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-prod/test_global_settings.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/hub-prod/test_switch_and_fallback_env.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/storage/test_entry_point.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/storage/test_storage_access.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.1}/tests/storage/test_storage_basis.py +0 -0
- {lamindb_setup-1.0.3 → lamindb_setup-1.1.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.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: Setup & configure LaminDB.
|
|
5
5
|
Author-email: Lamin Labs <open-source@lamin.ai>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -11,13 +11,15 @@ Requires-Dist: dj_database_url>=1.3.0,<3.0.0
|
|
|
11
11
|
Requires-Dist: pydantic-settings
|
|
12
12
|
Requires-Dist: appdirs<2.0.0
|
|
13
13
|
Requires-Dist: requests
|
|
14
|
-
Requires-Dist: universal_pathlib==0.2.
|
|
14
|
+
Requires-Dist: universal_pathlib==0.2.6
|
|
15
15
|
Requires-Dist: botocore<2.0.0
|
|
16
16
|
Requires-Dist: supabase>=2.8.1,<=2.11.0
|
|
17
|
+
Requires-Dist: storage3!=0.11.2; python_version < '3.11'
|
|
17
18
|
Requires-Dist: psutil
|
|
19
|
+
Requires-Dist: packaging
|
|
18
20
|
Requires-Dist: urllib3<2 ; extra == "aws"
|
|
19
21
|
Requires-Dist: aiobotocore[boto3]>=2.5.4,<3.0.0 ; extra == "aws"
|
|
20
|
-
Requires-Dist: s3fs>=2023.12.2,<=
|
|
22
|
+
Requires-Dist: s3fs>=2023.12.2,<=2025.2.0,!=2024.10.0 ; extra == "aws"
|
|
21
23
|
Requires-Dist: line_profiler ; extra == "dev"
|
|
22
24
|
Requires-Dist: pyjwt<3.0.0 ; extra == "dev"
|
|
23
25
|
Requires-Dist: psycopg2-binary ; extra == "dev"
|
|
@@ -29,7 +31,7 @@ Requires-Dist: pytest-xdist ; extra == "dev"
|
|
|
29
31
|
Requires-Dist: nbproject-test>=0.4.3 ; extra == "dev"
|
|
30
32
|
Requires-Dist: pandas ; extra == "dev"
|
|
31
33
|
Requires-Dist: django-schema-graph ; extra == "erdiagram"
|
|
32
|
-
Requires-Dist: gcsfs>=2023.12.2,<=
|
|
34
|
+
Requires-Dist: gcsfs>=2023.12.2,<=2025.2.0 ; extra == "gcp"
|
|
33
35
|
Project-URL: Home, https://github.com/laminlabs/lamindb-setup
|
|
34
36
|
Provides-Extra: aws
|
|
35
37
|
Provides-Extra: dev
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"cells": [
|
|
3
3
|
{
|
|
4
|
-
"attachments": {},
|
|
5
4
|
"cell_type": "markdown",
|
|
6
5
|
"metadata": {
|
|
7
6
|
"tags": []
|
|
@@ -107,7 +106,7 @@
|
|
|
107
106
|
"name": "python",
|
|
108
107
|
"nbconvert_exporter": "python",
|
|
109
108
|
"pygments_lexer": "ipython3",
|
|
110
|
-
"version": "3.10.
|
|
109
|
+
"version": "3.10.16"
|
|
111
110
|
},
|
|
112
111
|
"vscode": {
|
|
113
112
|
"interpreter": {
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
"metadata": {},
|
|
33
33
|
"outputs": [],
|
|
34
34
|
"source": [
|
|
35
|
-
"import lamindb_setup as ln_setup"
|
|
35
|
+
"import lamindb_setup as ln_setup\n",
|
|
36
|
+
"from lamindb_setup._check_setup import ModuleWasntConfigured\n",
|
|
37
|
+
"import pytest"
|
|
36
38
|
]
|
|
37
39
|
},
|
|
38
40
|
{
|
|
@@ -51,6 +53,20 @@
|
|
|
51
53
|
"If the user is the instance owner, load the instance by name:"
|
|
52
54
|
]
|
|
53
55
|
},
|
|
56
|
+
{
|
|
57
|
+
"cell_type": "code",
|
|
58
|
+
"execution_count": null,
|
|
59
|
+
"id": "1106eff3",
|
|
60
|
+
"metadata": {},
|
|
61
|
+
"outputs": [],
|
|
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",
|
|
67
|
+
"import bionty"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
54
70
|
{
|
|
55
71
|
"cell_type": "code",
|
|
56
72
|
"execution_count": null,
|
|
@@ -58,7 +74,38 @@
|
|
|
58
74
|
"metadata": {},
|
|
59
75
|
"outputs": [],
|
|
60
76
|
"source": [
|
|
61
|
-
"ln_setup.connect(\"
|
|
77
|
+
"ln_setup.connect(\n",
|
|
78
|
+
" \"mydata\", _reload_lamindb=False\n",
|
|
79
|
+
") # also test passing _reload_lamindb explicitly"
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
"cell_type": "code",
|
|
84
|
+
"execution_count": null,
|
|
85
|
+
"id": "86a1cb7c",
|
|
86
|
+
"metadata": {},
|
|
87
|
+
"outputs": [],
|
|
88
|
+
"source": [
|
|
89
|
+
"# wetlab is not in the (schema) modules of mydata\n",
|
|
90
|
+
"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",
|
|
93
|
+
" import wetlab"
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"cell_type": "code",
|
|
98
|
+
"execution_count": null,
|
|
99
|
+
"id": "799740b9",
|
|
100
|
+
"metadata": {},
|
|
101
|
+
"outputs": [],
|
|
102
|
+
"source": [
|
|
103
|
+
"# wetlab is not in the (schema) modules of mydata\n",
|
|
104
|
+
"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"
|
|
62
109
|
]
|
|
63
110
|
},
|
|
64
111
|
{
|
|
@@ -122,7 +169,7 @@
|
|
|
122
169
|
"name": "python",
|
|
123
170
|
"nbconvert_exporter": "python",
|
|
124
171
|
"pygments_lexer": "ipython3",
|
|
125
|
-
"version": "3.
|
|
172
|
+
"version": "3.10.16"
|
|
126
173
|
},
|
|
127
174
|
"vscode": {
|
|
128
175
|
"interpreter": {
|
|
@@ -224,9 +224,13 @@
|
|
|
224
224
|
"metadata": {},
|
|
225
225
|
"outputs": [],
|
|
226
226
|
"source": [
|
|
227
|
-
"dir_sync_local = settings.paths.
|
|
227
|
+
"dir_sync_local = settings.paths.cloud_to_local_no_update(\n",
|
|
228
228
|
" dir_sync.as_posix(), cache_key=\"dir_cache/key\"\n",
|
|
229
|
-
")"
|
|
229
|
+
")\n",
|
|
230
|
+
"assert dir_sync_local == settings.cache_dir / \"dir_cache/key\"\n",
|
|
231
|
+
"\n",
|
|
232
|
+
"assert dir_sync.synchronize(dir_sync_local, just_check=True)\n",
|
|
233
|
+
"assert not dir_sync_local.exists()"
|
|
230
234
|
]
|
|
231
235
|
},
|
|
232
236
|
{
|
|
@@ -236,7 +240,7 @@
|
|
|
236
240
|
"metadata": {},
|
|
237
241
|
"outputs": [],
|
|
238
242
|
"source": [
|
|
239
|
-
"assert dir_sync_local
|
|
243
|
+
"assert dir_sync.synchronize(dir_sync_local, just_check=False)\n",
|
|
240
244
|
"assert dir_sync_local.is_dir()\n",
|
|
241
245
|
"assert num_files(dir_sync_local) == 2"
|
|
242
246
|
]
|
|
@@ -744,7 +748,19 @@
|
|
|
744
748
|
"outputs": [],
|
|
745
749
|
"source": [
|
|
746
750
|
"hf_path = UPath(\"hf://datasets/Koncopd/lamindb-test@main/anndata/pbmc68k_test.h5ad\")\n",
|
|
747
|
-
"hf_path_local = settings.paths.
|
|
751
|
+
"hf_path_local = settings.paths.cloud_to_local_no_update(hf_path)\n",
|
|
752
|
+
"assert isinstance(hf_path_local, LocalPathClasses)"
|
|
753
|
+
]
|
|
754
|
+
},
|
|
755
|
+
{
|
|
756
|
+
"cell_type": "code",
|
|
757
|
+
"execution_count": null,
|
|
758
|
+
"id": "b0aad1f1",
|
|
759
|
+
"metadata": {},
|
|
760
|
+
"outputs": [],
|
|
761
|
+
"source": [
|
|
762
|
+
"assert hf_path.synchronize(hf_path_local, just_check=True)\n",
|
|
763
|
+
"assert not hf_path_local.exists()"
|
|
748
764
|
]
|
|
749
765
|
},
|
|
750
766
|
{
|
|
@@ -754,7 +770,7 @@
|
|
|
754
770
|
"metadata": {},
|
|
755
771
|
"outputs": [],
|
|
756
772
|
"source": [
|
|
757
|
-
"assert
|
|
773
|
+
"assert hf_path.synchronize(hf_path_local)\n",
|
|
758
774
|
"assert hf_path_local.is_file()"
|
|
759
775
|
]
|
|
760
776
|
},
|
|
@@ -811,7 +827,7 @@
|
|
|
811
827
|
"name": "python",
|
|
812
828
|
"nbconvert_exporter": "python",
|
|
813
829
|
"pygments_lexer": "ipython3",
|
|
814
|
-
"version": "3.
|
|
830
|
+
"version": "3.10.16"
|
|
815
831
|
},
|
|
816
832
|
"vscode": {
|
|
817
833
|
"interpreter": {
|
|
@@ -33,10 +33,12 @@ Modules & settings:
|
|
|
33
33
|
|
|
34
34
|
"""
|
|
35
35
|
|
|
36
|
-
__version__ = "1.
|
|
36
|
+
__version__ = "1.1.1" # denote a release candidate for 0.1.0 with 0.1rc1
|
|
37
37
|
|
|
38
38
|
import os
|
|
39
39
|
|
|
40
|
+
from packaging import version as packaging_version
|
|
41
|
+
|
|
40
42
|
from . import core
|
|
41
43
|
from ._check_setup import _check_instance_setup
|
|
42
44
|
from ._close import close
|
|
@@ -50,6 +52,20 @@ from ._register_instance import register
|
|
|
50
52
|
from ._setup_user import login, logout
|
|
51
53
|
from .core._settings import settings
|
|
52
54
|
|
|
55
|
+
# check that the version of s3fs is higher than the lower bound
|
|
56
|
+
# needed because spatialdata installs old versions of s3fs
|
|
57
|
+
try:
|
|
58
|
+
from s3fs import __version__ as s3fs_version
|
|
59
|
+
|
|
60
|
+
if packaging_version.parse(s3fs_version) < packaging_version.parse("2023.12.2"):
|
|
61
|
+
raise RuntimeError(
|
|
62
|
+
f"The version of s3fs you have ({s3fs_version}) is impompatible "
|
|
63
|
+
"with lamindb, please upgrade it: pip install s3fs>=2023.12.2"
|
|
64
|
+
)
|
|
65
|
+
except ImportError:
|
|
66
|
+
# might be not installed
|
|
67
|
+
pass
|
|
68
|
+
|
|
53
69
|
|
|
54
70
|
def _is_CI_environment() -> bool:
|
|
55
71
|
ci_env_vars = [
|
|
@@ -2,13 +2,14 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import functools
|
|
4
4
|
import importlib as il
|
|
5
|
+
import inspect
|
|
5
6
|
import os
|
|
6
7
|
from typing import TYPE_CHECKING
|
|
7
8
|
|
|
8
9
|
from lamin_utils import logger
|
|
9
10
|
|
|
10
11
|
from ._silence_loggers import silence_loggers
|
|
11
|
-
from .core import django
|
|
12
|
+
from .core import django as django_lamin
|
|
12
13
|
from .core._settings import settings
|
|
13
14
|
from .core._settings_store import current_instance_settings_file
|
|
14
15
|
from .core.exceptions import DefaultMessageException
|
|
@@ -33,8 +34,12 @@ CURRENT_ISETTINGS: InstanceSettings | None = None
|
|
|
33
34
|
IS_LOADING: bool = False
|
|
34
35
|
|
|
35
36
|
|
|
37
|
+
class ModuleWasntConfigured(SystemExit):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
36
41
|
# decorator to disable auto-connect when importing a module such as lamindb
|
|
37
|
-
def
|
|
42
|
+
def disable_auto_connect(func: Callable):
|
|
38
43
|
@functools.wraps(func)
|
|
39
44
|
def wrapper(*args, **kwargs):
|
|
40
45
|
global IS_LOADING
|
|
@@ -70,14 +75,66 @@ def _get_current_instance_settings() -> InstanceSettings | None:
|
|
|
70
75
|
return None
|
|
71
76
|
|
|
72
77
|
|
|
78
|
+
def _normalize_module_name(module_name: str) -> str:
|
|
79
|
+
return module_name.replace("lnschema_", "").replace("_", "-")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# checks that the provided modules is in the modules of the provided instance
|
|
83
|
+
# or in the apps setup by django
|
|
84
|
+
def _check_module_in_instance_modules(
|
|
85
|
+
module: str, isettings: InstanceSettings | None = None
|
|
86
|
+
) -> None:
|
|
87
|
+
not_in_instance_msg = (
|
|
88
|
+
f"'{module}' is missing from this instance. "
|
|
89
|
+
"Please go to your instance settings page and add it under 'schema modules'."
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
if isettings is not None:
|
|
93
|
+
modules_raw = isettings.modules
|
|
94
|
+
modules = set(modules_raw).union(
|
|
95
|
+
_normalize_module_name(module) for module in modules_raw
|
|
96
|
+
)
|
|
97
|
+
if _normalize_module_name(module) not in modules and module not in modules:
|
|
98
|
+
raise ModuleWasntConfigured(not_in_instance_msg)
|
|
99
|
+
else:
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
from django.apps import apps
|
|
103
|
+
|
|
104
|
+
for app in apps.get_app_configs():
|
|
105
|
+
# app.name is always unnormalized module (python package) name
|
|
106
|
+
if module == app.name or module == _normalize_module_name(app.name):
|
|
107
|
+
return
|
|
108
|
+
raise ModuleWasntConfigured(not_in_instance_msg)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# infer the name of the module that calls this function
|
|
112
|
+
def _infer_callers_module_name() -> str | None:
|
|
113
|
+
stack = inspect.stack()
|
|
114
|
+
if len(stack) < 3:
|
|
115
|
+
return None
|
|
116
|
+
module = inspect.getmodule(stack[2][0])
|
|
117
|
+
return module.__name__.partition(".")[0] if module is not None else None
|
|
118
|
+
|
|
119
|
+
|
|
73
120
|
# we make this a private function because in all the places it's used,
|
|
74
121
|
# users should not see it
|
|
75
122
|
def _check_instance_setup(from_module: str | None = None) -> bool:
|
|
76
|
-
if
|
|
123
|
+
if django_lamin.IS_SETUP:
|
|
77
124
|
# reload logic here because module might not yet have been imported
|
|
78
125
|
# upon first setup
|
|
79
|
-
if from_module is not None
|
|
80
|
-
|
|
126
|
+
if from_module is not None:
|
|
127
|
+
if from_module != "lamindb":
|
|
128
|
+
_check_module_in_instance_modules(from_module)
|
|
129
|
+
il.reload(il.import_module(from_module))
|
|
130
|
+
else:
|
|
131
|
+
infer_module = _infer_callers_module_name()
|
|
132
|
+
if infer_module is not None and infer_module not in {
|
|
133
|
+
"lamindb",
|
|
134
|
+
"lamindb_setup",
|
|
135
|
+
"lamin_cli",
|
|
136
|
+
}:
|
|
137
|
+
_check_module_in_instance_modules(infer_module)
|
|
81
138
|
return True
|
|
82
139
|
silence_loggers()
|
|
83
140
|
if os.environ.get("LAMINDB_MULTI_INSTANCE") == "true":
|
|
@@ -91,17 +148,19 @@ def _check_instance_setup(from_module: str | None = None) -> bool:
|
|
|
91
148
|
if (
|
|
92
149
|
from_module is not None
|
|
93
150
|
and settings.auto_connect
|
|
94
|
-
and not
|
|
151
|
+
and not django_lamin.IS_SETUP
|
|
95
152
|
and not IS_LOADING
|
|
96
153
|
):
|
|
97
|
-
if
|
|
154
|
+
if from_module != "lamindb":
|
|
155
|
+
_check_module_in_instance_modules(from_module, isettings)
|
|
156
|
+
|
|
98
157
|
import lamindb
|
|
99
158
|
|
|
100
159
|
il.reload(il.import_module(from_module))
|
|
101
160
|
else:
|
|
102
|
-
|
|
161
|
+
django_lamin.setup_django(isettings)
|
|
103
162
|
logger.important(f"connected lamindb: {isettings.slug}")
|
|
104
|
-
return
|
|
163
|
+
return django_lamin.IS_SETUP
|
|
105
164
|
else:
|
|
106
165
|
if from_module is not None and settings.auto_connect:
|
|
107
166
|
logger.warning(InstanceNotSetupError.default_message)
|
|
@@ -271,6 +271,7 @@ def connect(instance: str | None = None, **kwargs) -> str | tuple | None:
|
|
|
271
271
|
settings_dir / f"no_lnschema_core-{isettings.slug.replace('/', '--')}"
|
|
272
272
|
)
|
|
273
273
|
if not no_lnschema_core_file.exists():
|
|
274
|
+
# sqlite file for cloud sqlite instances is already updated here
|
|
274
275
|
migrate_lnschema_core(
|
|
275
276
|
isettings, no_lnschema_core_file, write_file=_write_settings
|
|
276
277
|
)
|
|
@@ -311,7 +312,8 @@ def connect(instance: str | None = None, **kwargs) -> str | tuple | None:
|
|
|
311
312
|
load_from_isettings(isettings, user=_user, write_settings=_write_settings)
|
|
312
313
|
if _reload_lamindb:
|
|
313
314
|
importlib.reload(importlib.import_module("lamindb"))
|
|
314
|
-
|
|
315
|
+
else:
|
|
316
|
+
logger.important(f"connected lamindb: {isettings.slug}")
|
|
315
317
|
except Exception as e:
|
|
316
318
|
if isettings is not None:
|
|
317
319
|
if _write_settings:
|
|
@@ -339,6 +341,12 @@ def migrate_lnschema_core(
|
|
|
339
341
|
"""Migrate lnschema_core tables to lamindb tables."""
|
|
340
342
|
from urllib.parse import urlparse
|
|
341
343
|
|
|
344
|
+
# we need to do this because the sqlite file should be already synced
|
|
345
|
+
# has no effect if not cloud sqlite
|
|
346
|
+
# errors if the sqlite file is not in the cloud and doesn't exist locally
|
|
347
|
+
# isettings.db syncs but doesn't error in this case due to error_no_origin=False
|
|
348
|
+
isettings._update_local_sqlite_file()
|
|
349
|
+
|
|
342
350
|
parsed_uri = urlparse(isettings.db)
|
|
343
351
|
db_type = parsed_uri.scheme
|
|
344
352
|
|
|
@@ -7,7 +7,7 @@ from uuid import UUID
|
|
|
7
7
|
from lamin_utils import logger
|
|
8
8
|
|
|
9
9
|
from ._connect_instance import _connect_instance, get_owner_name_from_identifier
|
|
10
|
-
from .core.
|
|
10
|
+
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
|
|
@@ -396,10 +396,5 @@ def infer_instance_name(
|
|
|
396
396
|
if storage == "create-s3":
|
|
397
397
|
raise ValueError("pass name to init if storage = 'create-s3'")
|
|
398
398
|
storage_path = UPath(storage).resolve()
|
|
399
|
-
|
|
400
|
-
if storage_path.name != "":
|
|
401
|
-
name = storage_path.name
|
|
402
|
-
else:
|
|
403
|
-
# dedicated treatment of bucket names
|
|
404
|
-
name = storage_path.drive
|
|
399
|
+
name = storage_path.path.rstrip("/").split("/")[-1]
|
|
405
400
|
return name.lower()
|
|
@@ -5,7 +5,7 @@ from django.db.migrations.loader import MigrationLoader
|
|
|
5
5
|
from lamin_utils import logger
|
|
6
6
|
from packaging import version
|
|
7
7
|
|
|
8
|
-
from ._check_setup import _check_instance_setup,
|
|
8
|
+
from ._check_setup import _check_instance_setup, disable_auto_connect
|
|
9
9
|
from .core._settings import settings
|
|
10
10
|
from .core.django import setup_django
|
|
11
11
|
|
|
@@ -62,7 +62,7 @@ class migrate:
|
|
|
62
62
|
"""
|
|
63
63
|
|
|
64
64
|
@classmethod
|
|
65
|
-
@
|
|
65
|
+
@disable_auto_connect
|
|
66
66
|
def create(cls) -> None:
|
|
67
67
|
"""Create a migration."""
|
|
68
68
|
if _check_instance_setup():
|
|
@@ -70,7 +70,7 @@ class migrate:
|
|
|
70
70
|
setup_django(settings.instance, create_migrations=True)
|
|
71
71
|
|
|
72
72
|
@classmethod
|
|
73
|
-
@
|
|
73
|
+
@disable_auto_connect
|
|
74
74
|
def deploy(cls) -> None:
|
|
75
75
|
"""Deploy a migration."""
|
|
76
76
|
from ._schema_metadata import update_schema_in_hub
|
|
@@ -115,7 +115,7 @@ class migrate:
|
|
|
115
115
|
)
|
|
116
116
|
|
|
117
117
|
@classmethod
|
|
118
|
-
@
|
|
118
|
+
@disable_auto_connect
|
|
119
119
|
def check(cls) -> bool:
|
|
120
120
|
"""Check whether Registry definitions are in sync with migrations."""
|
|
121
121
|
from django.core.management import call_command
|
|
@@ -132,7 +132,7 @@ class migrate:
|
|
|
132
132
|
return True
|
|
133
133
|
|
|
134
134
|
@classmethod
|
|
135
|
-
@
|
|
135
|
+
@disable_auto_connect
|
|
136
136
|
def squash(
|
|
137
137
|
cls, package_name, migration_nr, start_migration_nr: str | None = None
|
|
138
138
|
) -> None:
|
|
@@ -148,7 +148,7 @@ class migrate:
|
|
|
148
148
|
call_command("squashmigrations", package_name, migration_nr)
|
|
149
149
|
|
|
150
150
|
@classmethod
|
|
151
|
-
@
|
|
151
|
+
@disable_auto_connect
|
|
152
152
|
def show(cls) -> None:
|
|
153
153
|
"""Show migrations."""
|
|
154
154
|
from django.core.management import call_command
|
|
@@ -4,7 +4,7 @@ import os
|
|
|
4
4
|
import time
|
|
5
5
|
|
|
6
6
|
from lamin_utils import logger
|
|
7
|
-
from upath
|
|
7
|
+
from upath import UPath
|
|
8
8
|
|
|
9
9
|
HOSTED_REGIONS = [
|
|
10
10
|
"eu-central-1",
|
|
@@ -25,22 +25,29 @@ def _keep_trailing_slash(path_str: str):
|
|
|
25
25
|
return path_str if path_str[-1] == "/" else path_str + "/"
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
AWS_CREDENTIALS_EXPIRATION = 11 * 60 * 60 # refresh credentials after 11 hours
|
|
28
|
+
AWS_CREDENTIALS_EXPIRATION: int = 11 * 60 * 60 # refresh credentials after 11 hours
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
# set anon=True for these buckets if credentials fail for a public bucket
|
|
32
32
|
# to be expanded
|
|
33
|
-
PUBLIC_BUCKETS = ("cellxgene-data-public",)
|
|
33
|
+
PUBLIC_BUCKETS: tuple[str] = ("cellxgene-data-public",)
|
|
34
34
|
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
# s3-comaptible endpoints managed by lamin
|
|
37
|
+
# None means the standard aws s3 endpoint
|
|
38
|
+
LAMIN_ENDPOINTS: tuple[str | None] = (None,)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class AWSOptionsManager:
|
|
37
42
|
def __init__(self):
|
|
38
43
|
self._credentials_cache = {}
|
|
39
44
|
|
|
40
45
|
from s3fs import S3FileSystem
|
|
41
46
|
|
|
42
47
|
# this is cached so will be resued with the connection initialized
|
|
43
|
-
fs = S3FileSystem(
|
|
48
|
+
fs = S3FileSystem(
|
|
49
|
+
cache_regions=True, use_listings_cache=True, version_aware=False
|
|
50
|
+
)
|
|
44
51
|
try:
|
|
45
52
|
fs.connect()
|
|
46
53
|
self.anon: bool = fs.session._credentials is None
|
|
@@ -83,7 +90,7 @@ class AWSCredentialsManager:
|
|
|
83
90
|
def _get_cached_credentials(self, root: str) -> dict:
|
|
84
91
|
return self._credentials_cache[root]["credentials"]
|
|
85
92
|
|
|
86
|
-
def _path_inject_options(self, path:
|
|
93
|
+
def _path_inject_options(self, path: UPath, credentials: dict) -> UPath:
|
|
87
94
|
if credentials == {}:
|
|
88
95
|
# credentials were specified manually for the path
|
|
89
96
|
if "anon" in path.storage_options:
|
|
@@ -99,13 +106,27 @@ class AWSCredentialsManager:
|
|
|
99
106
|
connection_options = credentials
|
|
100
107
|
|
|
101
108
|
if "cache_regions" in path.storage_options:
|
|
102
|
-
cache_regions = path.storage_options["cache_regions"]
|
|
109
|
+
connection_options["cache_regions"] = path.storage_options["cache_regions"]
|
|
103
110
|
else:
|
|
104
|
-
cache_regions =
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
connection_options["cache_regions"] = (
|
|
112
|
+
path.storage_options.get("endpoint_url", None) is None
|
|
113
|
+
)
|
|
114
|
+
# we use cache to avoid some uneeded downloads or credential problems
|
|
115
|
+
# see in upload_from
|
|
116
|
+
connection_options["use_listings_cache"] = path.storage_options.get(
|
|
117
|
+
"use_listings_cache", True
|
|
118
|
+
)
|
|
119
|
+
# normally we want to ignore objects vsrsions in a versioned bucket
|
|
120
|
+
connection_options["version_aware"] = path.storage_options.get(
|
|
121
|
+
"version_aware", False
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
return UPath(path, **connection_options)
|
|
125
|
+
|
|
126
|
+
def enrich_path(self, path: UPath, access_token: str | None = None) -> UPath:
|
|
127
|
+
# ignore paths with non-lamin-managed endpoints
|
|
128
|
+
if path.storage_options.get("endpoint_url", None) not in LAMIN_ENDPOINTS:
|
|
129
|
+
return path
|
|
109
130
|
# trailing slash is needed to avoid returning incorrect results
|
|
110
131
|
# with .startswith
|
|
111
132
|
# for example s3://lamindata-eu should not receive cache for s3://lamindata
|
|
@@ -160,13 +181,13 @@ class AWSCredentialsManager:
|
|
|
160
181
|
return self._path_inject_options(path, credentials)
|
|
161
182
|
|
|
162
183
|
|
|
163
|
-
|
|
184
|
+
_aws_options_manager: AWSOptionsManager | None = None
|
|
164
185
|
|
|
165
186
|
|
|
166
|
-
def
|
|
167
|
-
global
|
|
187
|
+
def get_aws_options_manager() -> AWSOptionsManager:
|
|
188
|
+
global _aws_options_manager
|
|
168
189
|
|
|
169
|
-
if
|
|
170
|
-
|
|
190
|
+
if _aws_options_manager is None:
|
|
191
|
+
_aws_options_manager = AWSOptionsManager()
|
|
171
192
|
|
|
172
|
-
return
|
|
193
|
+
return _aws_options_manager
|
|
@@ -66,7 +66,11 @@ def connect_hub(
|
|
|
66
66
|
) -> Client:
|
|
67
67
|
env = Environment(fallback=fallback_env)
|
|
68
68
|
if client_options is None:
|
|
69
|
-
|
|
69
|
+
# function_client_timeout=5 by default
|
|
70
|
+
# increase to avoid rare timeouts for edge functions
|
|
71
|
+
client_options = ClientOptions(
|
|
72
|
+
auto_refresh_token=False, function_client_timeout=10
|
|
73
|
+
)
|
|
70
74
|
return create_client(env.supabase_api_url, env.supabase_anon_key, client_options)
|
|
71
75
|
|
|
72
76
|
|
|
@@ -311,6 +311,7 @@ def _init_instance(
|
|
|
311
311
|
"db_database": db_dsn.db.database,
|
|
312
312
|
}
|
|
313
313
|
fields.update(db_fields)
|
|
314
|
+
slug = isettings.slug
|
|
314
315
|
# I'd like the following to be an upsert, but this seems to violate RLS
|
|
315
316
|
# Similarly, if we don't specify `returning="minimal"`, we'll violate RLS
|
|
316
317
|
# we could make this idempotent by catching an error, but this seems dangerous
|
|
@@ -318,14 +319,13 @@ def _init_instance(
|
|
|
318
319
|
try:
|
|
319
320
|
client.table("instance").insert(fields, returning="minimal").execute()
|
|
320
321
|
except APIError:
|
|
321
|
-
logger.warning(
|
|
322
|
-
f"instance already existed at: https://lamin.ai/{isettings.owner}/{isettings.name}"
|
|
323
|
-
)
|
|
322
|
+
logger.warning(f"instance already existed at: https://lamin.ai/{slug}")
|
|
324
323
|
return None
|
|
325
324
|
client.table("storage").update(
|
|
326
325
|
{"instance_id": isettings._id.hex, "is_default": True}
|
|
327
326
|
).eq("id", isettings.storage._uuid.hex).execute() # type: ignore
|
|
328
|
-
|
|
327
|
+
if isettings.dialect != "sqlite" and isettings.is_remote:
|
|
328
|
+
logger.important(f"go to: https://lamin.ai/{slug}")
|
|
329
329
|
|
|
330
330
|
|
|
331
331
|
def _connect_instance_hub(
|
|
@@ -34,7 +34,6 @@ def private_django_api(reverse=False):
|
|
|
34
34
|
# the order here matters
|
|
35
35
|
# changing it might break the tests
|
|
36
36
|
attributes = [
|
|
37
|
-
"DoesNotExist",
|
|
38
37
|
"MultipleObjectsReturned",
|
|
39
38
|
"add_to_class",
|
|
40
39
|
"adelete",
|
|
@@ -72,10 +71,6 @@ def private_django_api(reverse=False):
|
|
|
72
71
|
new_name = attr if reverse else f"_{attr}"
|
|
73
72
|
content = content.replace(old_name, new_name)
|
|
74
73
|
|
|
75
|
-
if not reverse:
|
|
76
|
-
content = content.replace("Field_DoesNotExist", "FieldDoesNotExist")
|
|
77
|
-
content = content.replace("Object_DoesNotExist", "ObjectDoesNotExist")
|
|
78
|
-
|
|
79
74
|
if content != original_content:
|
|
80
75
|
file_path.write_text(content, encoding=encoding)
|
|
81
76
|
|