lamindb_setup 0.76.8__tar.gz → 0.77.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.
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/.github/workflows/build.yml +7 -7
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/PKG-INFO +1 -1
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/07-keep-artifacts-local.ipynb +16 -12
- lamindb_setup-0.77.0/docs/hub-prod/test-init-load-local-anonymously.ipynb +140 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-prod/test-insufficient-user-info.ipynb +8 -15
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-prod/test-sqlite-lock.ipynb +7 -7
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/notebooks.md +1 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/__init__.py +6 -7
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_connect_instance.py +11 -4
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_setup_user.py +55 -39
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_hub_client.py +11 -2
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_hub_core.py +56 -5
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_settings_load.py +13 -8
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_settings_store.py +1 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_settings_user.py +3 -1
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/noxfile.py +1 -1
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-cloud/test_connect_instance.py +9 -0
- lamindb_setup-0.77.0/tests/hub-cloud/test_login.py +67 -0
- lamindb_setup-0.76.8/tests/hub-cloud/test_login.py +0 -19
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/.github/workflows/doc-changes.yml +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/.gitignore +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/.pre-commit-config.yaml +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/LICENSE +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/README.md +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/changelog.md +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/01-init-local-instance.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/02-connect-local-instance.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/03-add-managed-storage.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/04-test-bionty.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/05-init-hosted-instance.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/06-connect-hosted-instance.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/08-test-multi-session.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/test_notebooks.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-prod/test-cache-management.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-prod/test-cloud-sync.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-prod/test-connect-anonymously.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-prod/test-empty-init.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-prod/test-import-schema.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-prod/test-invalid-schema.ipynb +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-prod/test_notebooks2.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/index.md +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/reference.md +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_cache.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_check.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_check_setup.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_close.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_delete.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_django.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_exportdb.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_importdb.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_init_instance.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_migrate.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_register_instance.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_schema.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_schema_metadata.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_set_managed_storage.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/_silence_loggers.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/__init__.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_aws_credentials.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_aws_storage.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_deprecated.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_docs.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_hub_crud.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_hub_utils.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_private_django_api.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_settings.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_settings_instance.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_settings_save.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_settings_storage.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/_setup_bionty_sources.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/cloud_sqlite_locker.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/django.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/exceptions.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/hashing.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/types.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/lamindb_setup/core/upath.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/pyproject.toml +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-cloud/scripts/script-to-fail-managed-storage.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-cloud/test_delete_instance.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-cloud/test_fail_managed_storage.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-cloud/test_init_instance.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-cloud/test_migrate.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-cloud/test_set_storage.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-local/conftest.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-local/test_all.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-local/test_update_schema_in_hub.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-prod/conftest.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-prod/test_django.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-prod/test_global_settings.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-prod/test_switch_and_fallback_env.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-prod/test_upath.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/storage/test_hashing.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/storage/test_storage_access.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/storage/test_storage_basis.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/storage/test_storage_stats.py +0 -0
- {lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/storage/test_to_url.py +0 -0
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
with:
|
|
20
20
|
python-version: "3.10" # run one job on 3.9
|
|
21
21
|
cache: "pip"
|
|
22
|
-
- uses: aws-actions/configure-aws-credentials@
|
|
22
|
+
- uses: aws-actions/configure-aws-credentials@v4
|
|
23
23
|
with:
|
|
24
24
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
25
25
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
@@ -45,7 +45,7 @@ jobs:
|
|
|
45
45
|
python-version: "3.10" # test on 3.9
|
|
46
46
|
timeout-minutes: 7
|
|
47
47
|
steps:
|
|
48
|
-
- uses: aws-actions/configure-aws-credentials@
|
|
48
|
+
- uses: aws-actions/configure-aws-credentials@v4
|
|
49
49
|
with:
|
|
50
50
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
51
51
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
@@ -62,12 +62,12 @@ jobs:
|
|
|
62
62
|
token: ${{ secrets.GH_TOKEN_DEPLOY_LAMINAPP }}
|
|
63
63
|
path: laminhub
|
|
64
64
|
ref: main
|
|
65
|
-
- uses: actions/cache@
|
|
65
|
+
- uses: actions/cache@v4
|
|
66
66
|
with:
|
|
67
67
|
path: ~/.cache/pre-commit
|
|
68
68
|
key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}
|
|
69
69
|
- id: cache-postgres
|
|
70
|
-
uses: actions/cache@
|
|
70
|
+
uses: actions/cache@v4
|
|
71
71
|
with:
|
|
72
72
|
path: ~/postgres.tar
|
|
73
73
|
key: cache-postgres-0
|
|
@@ -118,7 +118,7 @@ jobs:
|
|
|
118
118
|
runs-on: ubuntu-latest
|
|
119
119
|
timeout-minutes: 6
|
|
120
120
|
steps:
|
|
121
|
-
- uses: aws-actions/configure-aws-credentials@
|
|
121
|
+
- uses: aws-actions/configure-aws-credentials@v4
|
|
122
122
|
with:
|
|
123
123
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
124
124
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
@@ -144,7 +144,7 @@ jobs:
|
|
|
144
144
|
- run: pip install "laminci@git+https://x-access-token:${{ secrets.LAMIN_BUILD_DOCS }}@github.com/laminlabs/laminci"
|
|
145
145
|
- run: nox -s "install(group='hub-local')"
|
|
146
146
|
- id: cache-supabase
|
|
147
|
-
uses: actions/cache@
|
|
147
|
+
uses: actions/cache@v4
|
|
148
148
|
with:
|
|
149
149
|
path: /var/lib/docker
|
|
150
150
|
key: cache-supabase
|
|
@@ -183,7 +183,7 @@ jobs:
|
|
|
183
183
|
needs: hub-cloud
|
|
184
184
|
runs-on: ubuntu-latest
|
|
185
185
|
steps:
|
|
186
|
-
- uses: aws-actions/configure-aws-credentials@
|
|
186
|
+
- uses: aws-actions/configure-aws-credentials@v4
|
|
187
187
|
with:
|
|
188
188
|
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
189
189
|
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
@@ -16,22 +16,26 @@
|
|
|
16
16
|
"# this shouldn't run in a notebook, but in the unit tests\n",
|
|
17
17
|
"# currently still limited because of inability to load multiple instances\n",
|
|
18
18
|
"\n",
|
|
19
|
+
"import os\n",
|
|
19
20
|
"import pytest\n",
|
|
20
21
|
"import lamindb_setup as ln_setup\n",
|
|
21
|
-
"from
|
|
22
|
+
"from upath import UPath\n",
|
|
23
|
+
"\n",
|
|
24
|
+
"name = f\"keep-artifacts-local-setup-{os.environ['LAMIN_ENV']}\"\n",
|
|
25
|
+
"storage = (UPath(\"s3://lamindb-ci\") / name).as_posix()\n",
|
|
22
26
|
"\n",
|
|
23
27
|
"ln_setup.login(\"testuser1\")\n",
|
|
24
|
-
"ln_setup.init(storage
|
|
28
|
+
"ln_setup.init(storage=storage)\n",
|
|
25
29
|
"\n",
|
|
26
|
-
"assert ln_setup.settings.instance.name == \
|
|
30
|
+
"assert ln_setup.settings.instance.name == name\n",
|
|
27
31
|
"assert ln_setup.settings.instance.storage.type_is_cloud\n",
|
|
28
32
|
"assert (\n",
|
|
29
33
|
" ln_setup.settings.instance.storage.root_as_str\n",
|
|
30
|
-
" == \
|
|
34
|
+
" == storage\n",
|
|
31
35
|
")\n",
|
|
32
36
|
"assert (\n",
|
|
33
37
|
" ln_setup.settings.instance._sqlite_file.as_posix()\n",
|
|
34
|
-
" == f\"
|
|
38
|
+
" == f\"{storage}/{ln_setup.settings.instance._id.hex}.lndb\" # noqa\n",
|
|
35
39
|
")"
|
|
36
40
|
]
|
|
37
41
|
},
|
|
@@ -68,12 +72,12 @@
|
|
|
68
72
|
"\n",
|
|
69
73
|
"assert (\n",
|
|
70
74
|
" ln_setup.settings.instance.storage_local.root.as_posix()\n",
|
|
71
|
-
" ==
|
|
75
|
+
" == UPath(\"./my_storage_local\").resolve().as_posix()\n",
|
|
72
76
|
")\n",
|
|
73
77
|
"assert (ln_setup.settings.instance.storage_local.root / \".lamindb/_is_initialized\").read_text() == ln_setup.settings.instance.storage_local.uid\n",
|
|
74
78
|
"assert ln_setup.settings.instance.storage_local is not None\n",
|
|
75
79
|
"# the remote storage location is still in the regular slot\n",
|
|
76
|
-
"assert ln_setup.settings.instance.storage.root.as_posix() ==
|
|
80
|
+
"assert ln_setup.settings.instance.storage.root.as_posix() == storage"
|
|
77
81
|
]
|
|
78
82
|
},
|
|
79
83
|
{
|
|
@@ -100,7 +104,7 @@
|
|
|
100
104
|
"source": [
|
|
101
105
|
"assert (\n",
|
|
102
106
|
" ln_setup.settings.instance.storage_local.root.as_posix()\n",
|
|
103
|
-
" ==
|
|
107
|
+
" == UPath(\"./my_storage_local2\").resolve().as_posix()\n",
|
|
104
108
|
")\n",
|
|
105
109
|
"assert (ln_setup.settings.instance.storage_local.root / \".lamindb/_is_initialized\").read_text() == ln_setup.settings.instance.storage_local.uid"
|
|
106
110
|
]
|
|
@@ -170,7 +174,7 @@
|
|
|
170
174
|
"outputs": [],
|
|
171
175
|
"source": [
|
|
172
176
|
"with pytest.raises(ln_setup.core.upath.InstanceNotEmpty):\n",
|
|
173
|
-
" ln_setup.delete(
|
|
177
|
+
" ln_setup.delete(name, force=True)"
|
|
174
178
|
]
|
|
175
179
|
},
|
|
176
180
|
{
|
|
@@ -188,13 +192,13 @@
|
|
|
188
192
|
"metadata": {},
|
|
189
193
|
"outputs": [],
|
|
190
194
|
"source": [
|
|
191
|
-
"ln_setup.delete(
|
|
195
|
+
"ln_setup.delete(name, force=True)"
|
|
192
196
|
]
|
|
193
197
|
}
|
|
194
198
|
],
|
|
195
199
|
"metadata": {
|
|
196
200
|
"kernelspec": {
|
|
197
|
-
"display_name": "
|
|
201
|
+
"display_name": "Python 3 (ipykernel)",
|
|
198
202
|
"language": "python",
|
|
199
203
|
"name": "python3"
|
|
200
204
|
},
|
|
@@ -208,7 +212,7 @@
|
|
|
208
212
|
"name": "python",
|
|
209
213
|
"nbconvert_exporter": "python",
|
|
210
214
|
"pygments_lexer": "ipython3",
|
|
211
|
-
"version": "3.
|
|
215
|
+
"version": "3.9.17"
|
|
212
216
|
}
|
|
213
217
|
},
|
|
214
218
|
"nbformat": 4,
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "code",
|
|
5
|
+
"execution_count": null,
|
|
6
|
+
"id": "c4107648",
|
|
7
|
+
"metadata": {},
|
|
8
|
+
"outputs": [],
|
|
9
|
+
"source": [
|
|
10
|
+
"import lamindb_setup as ln_setup"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"cell_type": "markdown",
|
|
15
|
+
"id": "8587fb8e",
|
|
16
|
+
"metadata": {},
|
|
17
|
+
"source": [
|
|
18
|
+
"Check current user."
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"cell_type": "code",
|
|
23
|
+
"execution_count": null,
|
|
24
|
+
"id": "66c361b1",
|
|
25
|
+
"metadata": {},
|
|
26
|
+
"outputs": [],
|
|
27
|
+
"source": [
|
|
28
|
+
"ln_setup.login(\"testuser2\")\n",
|
|
29
|
+
"assert ln_setup.settings.user.handle == \"testuser2\""
|
|
30
|
+
]
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"cell_type": "markdown",
|
|
34
|
+
"id": "c6cc67f2",
|
|
35
|
+
"metadata": {},
|
|
36
|
+
"source": [
|
|
37
|
+
"logout and check that the user info was updated."
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"cell_type": "code",
|
|
42
|
+
"execution_count": null,
|
|
43
|
+
"id": "7a572ee2",
|
|
44
|
+
"metadata": {},
|
|
45
|
+
"outputs": [],
|
|
46
|
+
"source": [
|
|
47
|
+
"ln_setup.logout()\n",
|
|
48
|
+
"assert ln_setup.settings.user.handle == \"anonymous\""
|
|
49
|
+
]
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"cell_type": "markdown",
|
|
53
|
+
"id": "4b8b119f",
|
|
54
|
+
"metadata": {},
|
|
55
|
+
"source": [
|
|
56
|
+
"Init a local instance anonymously."
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"cell_type": "code",
|
|
61
|
+
"execution_count": null,
|
|
62
|
+
"id": "f8663b52",
|
|
63
|
+
"metadata": {},
|
|
64
|
+
"outputs": [],
|
|
65
|
+
"source": [
|
|
66
|
+
"!lamin close\n",
|
|
67
|
+
"!lamin init --storage ./test-anonymous-init --schema bionty"
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"cell_type": "markdown",
|
|
72
|
+
"id": "a9f53d46",
|
|
73
|
+
"metadata": {},
|
|
74
|
+
"source": [
|
|
75
|
+
"Load the instance and check."
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"cell_type": "code",
|
|
80
|
+
"execution_count": null,
|
|
81
|
+
"id": "1bb37c25",
|
|
82
|
+
"metadata": {},
|
|
83
|
+
"outputs": [],
|
|
84
|
+
"source": [
|
|
85
|
+
"ln_setup.load(\"test-anonymous-init\")\n",
|
|
86
|
+
"assert ln_setup.settings.instance.name == \"test-anonymous-init\"\n",
|
|
87
|
+
"assert ln_setup.settings.instance.owner == \"anonymous\""
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
"cell_type": "markdown",
|
|
92
|
+
"id": "9c19ede8",
|
|
93
|
+
"metadata": {},
|
|
94
|
+
"source": [
|
|
95
|
+
"Clean up."
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"cell_type": "code",
|
|
100
|
+
"execution_count": null,
|
|
101
|
+
"id": "b37256df",
|
|
102
|
+
"metadata": {},
|
|
103
|
+
"outputs": [],
|
|
104
|
+
"source": [
|
|
105
|
+
"ln_setup.delete(\"test-anonymous-init\", force=True)"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"cell_type": "code",
|
|
110
|
+
"execution_count": null,
|
|
111
|
+
"id": "c1eea364",
|
|
112
|
+
"metadata": {},
|
|
113
|
+
"outputs": [],
|
|
114
|
+
"source": [
|
|
115
|
+
"ln_setup.login(\"testuser2\")"
|
|
116
|
+
]
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
"metadata": {
|
|
120
|
+
"kernelspec": {
|
|
121
|
+
"display_name": "Python 3 (ipykernel)",
|
|
122
|
+
"language": "python",
|
|
123
|
+
"name": "python3"
|
|
124
|
+
},
|
|
125
|
+
"language_info": {
|
|
126
|
+
"codemirror_mode": {
|
|
127
|
+
"name": "ipython",
|
|
128
|
+
"version": 3
|
|
129
|
+
},
|
|
130
|
+
"file_extension": ".py",
|
|
131
|
+
"mimetype": "text/x-python",
|
|
132
|
+
"name": "python",
|
|
133
|
+
"nbconvert_exporter": "python",
|
|
134
|
+
"pygments_lexer": "ipython3",
|
|
135
|
+
"version": "3.9.17"
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
"nbformat": 4,
|
|
139
|
+
"nbformat_minor": 5
|
|
140
|
+
}
|
{lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-prod/test-insufficient-user-info.ipynb
RENAMED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"cells": [
|
|
3
3
|
{
|
|
4
|
-
"attachments": {},
|
|
5
4
|
"cell_type": "markdown",
|
|
6
5
|
"metadata": {},
|
|
7
6
|
"source": [
|
|
@@ -33,7 +32,6 @@
|
|
|
33
32
|
]
|
|
34
33
|
},
|
|
35
34
|
{
|
|
36
|
-
"attachments": {},
|
|
37
35
|
"cell_type": "markdown",
|
|
38
36
|
"metadata": {},
|
|
39
37
|
"source": [
|
|
@@ -46,16 +44,15 @@
|
|
|
46
44
|
"metadata": {},
|
|
47
45
|
"outputs": [],
|
|
48
46
|
"source": [
|
|
49
|
-
"with pytest.raises(
|
|
47
|
+
"with pytest.raises(ValueError):\n",
|
|
50
48
|
" ln_setup.login()"
|
|
51
49
|
]
|
|
52
50
|
},
|
|
53
51
|
{
|
|
54
|
-
"attachments": {},
|
|
55
52
|
"cell_type": "markdown",
|
|
56
53
|
"metadata": {},
|
|
57
54
|
"source": [
|
|
58
|
-
"If we add an email or handle it looks up
|
|
55
|
+
"If we add an email or handle it looks up credentials from the stored env file (`lamin login testuser1@lamin.ai`):"
|
|
59
56
|
]
|
|
60
57
|
},
|
|
61
58
|
{
|
|
@@ -68,7 +65,6 @@
|
|
|
68
65
|
]
|
|
69
66
|
},
|
|
70
67
|
{
|
|
71
|
-
"attachments": {},
|
|
72
68
|
"cell_type": "markdown",
|
|
73
69
|
"metadata": {},
|
|
74
70
|
"source": [
|
|
@@ -89,7 +85,6 @@
|
|
|
89
85
|
]
|
|
90
86
|
},
|
|
91
87
|
{
|
|
92
|
-
"attachments": {},
|
|
93
88
|
"cell_type": "markdown",
|
|
94
89
|
"metadata": {},
|
|
95
90
|
"source": [
|
|
@@ -102,16 +97,15 @@
|
|
|
102
97
|
"metadata": {},
|
|
103
98
|
"outputs": [],
|
|
104
99
|
"source": [
|
|
105
|
-
"with pytest.raises(
|
|
106
|
-
" ln_setup.login(\"testuser1\",
|
|
100
|
+
"with pytest.raises(SystemExit):\n",
|
|
101
|
+
" ln_setup.login(\"testuser1\", key=\"dummy\")"
|
|
107
102
|
]
|
|
108
103
|
},
|
|
109
104
|
{
|
|
110
|
-
"attachments": {},
|
|
111
105
|
"cell_type": "markdown",
|
|
112
106
|
"metadata": {},
|
|
113
107
|
"source": [
|
|
114
|
-
"If we try to login without
|
|
108
|
+
"If we try to login without key, this will error:"
|
|
115
109
|
]
|
|
116
110
|
},
|
|
117
111
|
{
|
|
@@ -120,16 +114,15 @@
|
|
|
120
114
|
"metadata": {},
|
|
121
115
|
"outputs": [],
|
|
122
116
|
"source": [
|
|
123
|
-
"with pytest.raises(
|
|
117
|
+
"with pytest.raises(SystemExit):\n",
|
|
124
118
|
" ln_setup.login(\"testuser1@lamin.ai\")"
|
|
125
119
|
]
|
|
126
120
|
},
|
|
127
121
|
{
|
|
128
|
-
"attachments": {},
|
|
129
122
|
"cell_type": "markdown",
|
|
130
123
|
"metadata": {},
|
|
131
124
|
"source": [
|
|
132
|
-
"If we try login with a wrong
|
|
125
|
+
"If we try login with a wrong key, this will error:"
|
|
133
126
|
]
|
|
134
127
|
},
|
|
135
128
|
{
|
|
@@ -186,7 +179,7 @@
|
|
|
186
179
|
"name": "python",
|
|
187
180
|
"nbconvert_exporter": "python",
|
|
188
181
|
"pygments_lexer": "ipython3",
|
|
189
|
-
"version": "3.
|
|
182
|
+
"version": "3.9.17"
|
|
190
183
|
},
|
|
191
184
|
"nbproject": {
|
|
192
185
|
"id": "vLOmzd6d4Jow",
|
|
@@ -11,24 +11,24 @@
|
|
|
11
11
|
{
|
|
12
12
|
"cell_type": "code",
|
|
13
13
|
"execution_count": null,
|
|
14
|
-
"id": "
|
|
14
|
+
"id": "69eeefee",
|
|
15
15
|
"metadata": {},
|
|
16
16
|
"outputs": [],
|
|
17
17
|
"source": [
|
|
18
|
-
"
|
|
18
|
+
"import pytest\n",
|
|
19
|
+
"import lamindb_setup as ln_setup\n",
|
|
20
|
+
"from lamindb_setup.core.cloud_sqlite_locker import Locker, InstanceLockedException\n",
|
|
21
|
+
"from lamindb_setup.core.upath import UPath"
|
|
19
22
|
]
|
|
20
23
|
},
|
|
21
24
|
{
|
|
22
25
|
"cell_type": "code",
|
|
23
26
|
"execution_count": null,
|
|
24
|
-
"id": "
|
|
27
|
+
"id": "5605dfcd",
|
|
25
28
|
"metadata": {},
|
|
26
29
|
"outputs": [],
|
|
27
30
|
"source": [
|
|
28
|
-
"
|
|
29
|
-
"import lamindb_setup as ln_setup\n",
|
|
30
|
-
"from lamindb_setup.core.cloud_sqlite_locker import Locker, InstanceLockedException\n",
|
|
31
|
-
"from lamindb_setup.core.upath import UPath"
|
|
31
|
+
"ln_setup.login(\"testuser2\")"
|
|
32
32
|
]
|
|
33
33
|
},
|
|
34
34
|
{
|
|
@@ -17,6 +17,7 @@ hub-prod/test-empty-init
|
|
|
17
17
|
hub-prod/test-import-schema
|
|
18
18
|
hub-prod/test-invalid-schema
|
|
19
19
|
hub-prod/test-insufficient-user-info
|
|
20
|
+
hub-prod/test-init-load-local-anonymously
|
|
20
21
|
hub-prod/test-connect-anonymously
|
|
21
22
|
hub-prod/test-sqlite-lock
|
|
22
23
|
hub-prod/test-cloud-sync
|
|
@@ -34,10 +34,10 @@ Modules & settings:
|
|
|
34
34
|
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
-
__version__ = "0.
|
|
37
|
+
__version__ = "0.77.0" # denote a release candidate for 0.1.0 with 0.1rc1
|
|
38
38
|
|
|
39
|
-
import
|
|
40
|
-
|
|
39
|
+
import os as _os
|
|
40
|
+
import sys as _sys
|
|
41
41
|
|
|
42
42
|
from . import core
|
|
43
43
|
from ._check_setup import _check_instance_setup
|
|
@@ -51,12 +51,11 @@ from ._register_instance import register
|
|
|
51
51
|
from ._setup_user import login, logout
|
|
52
52
|
from .core._settings import settings
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
_TESTING = False # used in lamindb tests
|
|
54
|
+
_TESTING = _os.getenv("LAMIN_TESTING") is not None
|
|
56
55
|
|
|
57
56
|
# hide the supabase error in a thread on windows
|
|
58
|
-
if
|
|
59
|
-
if
|
|
57
|
+
if _os.name == "nt":
|
|
58
|
+
if _sys.version_info.minor > 7:
|
|
60
59
|
import threading
|
|
61
60
|
|
|
62
61
|
_original_excepthook = threading.excepthook
|
|
@@ -124,7 +124,11 @@ def _connect_instance(
|
|
|
124
124
|
if make_hub_request:
|
|
125
125
|
# the following will return a string if the instance does not exist
|
|
126
126
|
# on the hub
|
|
127
|
-
|
|
127
|
+
# do not call hub if the user is anonymous
|
|
128
|
+
if owner != "anonymous":
|
|
129
|
+
hub_result = load_instance_from_hub(owner=owner, name=name)
|
|
130
|
+
else:
|
|
131
|
+
hub_result = "anonymous-user"
|
|
128
132
|
# if hub_result is not a string, it means it made a request
|
|
129
133
|
# that successfully returned metadata
|
|
130
134
|
if not isinstance(hub_result, str):
|
|
@@ -155,9 +159,12 @@ def _connect_instance(
|
|
|
155
159
|
)
|
|
156
160
|
check_whether_migrations_in_sync(instance_result["lamindb_version"])
|
|
157
161
|
else:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
162
|
+
if hub_result != "anonymous-user":
|
|
163
|
+
message = INSTANCE_NOT_FOUND_MESSAGE.format(
|
|
164
|
+
owner=owner, name=name, hub_result=hub_result
|
|
165
|
+
)
|
|
166
|
+
else:
|
|
167
|
+
message = "It is not possible to load an anonymous-owned instance from the hub"
|
|
161
168
|
if settings_file.exists():
|
|
162
169
|
isettings = load_instance_settings(settings_file)
|
|
163
170
|
if isettings.is_remote:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
import os
|
|
4
|
+
from typing import TYPE_CHECKING, Optional, Union
|
|
4
5
|
|
|
5
6
|
from lamin_utils import logger
|
|
6
7
|
|
|
@@ -15,8 +16,11 @@ from .core._settings_store import (
|
|
|
15
16
|
user_settings_file_handle,
|
|
16
17
|
)
|
|
17
18
|
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from lamindb_setup.core._settings_save import UserSettings
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
|
|
23
|
+
def load_user(email: str | None = None, handle: str | None = None) -> UserSettings:
|
|
20
24
|
if email is not None:
|
|
21
25
|
settings_file = user_settings_file_email(email)
|
|
22
26
|
if handle is not None:
|
|
@@ -28,8 +32,8 @@ def load_user(email: str | None = None, handle: str | None = None) -> str | None
|
|
|
28
32
|
else:
|
|
29
33
|
user_settings = load_or_create_user_settings()
|
|
30
34
|
if email is None:
|
|
31
|
-
raise
|
|
32
|
-
"Use your email for your first login in a compute environment. "
|
|
35
|
+
raise SystemExit(
|
|
36
|
+
"✗ Use your email for your first login in a compute environment. "
|
|
33
37
|
"After that, you can use your handle."
|
|
34
38
|
)
|
|
35
39
|
user_settings.email = email
|
|
@@ -40,59 +44,68 @@ def load_user(email: str | None = None, handle: str | None = None) -> str | None
|
|
|
40
44
|
|
|
41
45
|
settings._user_settings = None # this is to refresh a settings instance
|
|
42
46
|
|
|
43
|
-
return
|
|
47
|
+
return user_settings
|
|
44
48
|
|
|
45
49
|
|
|
46
50
|
def login(
|
|
47
|
-
user: str,
|
|
48
|
-
*,
|
|
49
|
-
key: str | None = None,
|
|
50
|
-
password: str | None = None, # for backward compat
|
|
51
|
+
user: str | None = None, *, key: str | None = None, api_key: str | None = None
|
|
51
52
|
) -> None:
|
|
52
53
|
"""Log in user.
|
|
53
54
|
|
|
54
55
|
Args:
|
|
55
56
|
user: handle or email
|
|
56
|
-
key: API key
|
|
57
|
-
|
|
57
|
+
key: API key
|
|
58
|
+
api_key: Beta API key
|
|
58
59
|
"""
|
|
59
|
-
if
|
|
60
|
-
|
|
60
|
+
if user is None and api_key is None:
|
|
61
|
+
if "LAMIN_API_KEY" in os.environ:
|
|
62
|
+
api_key = os.environ["LAMIN_API_KEY"]
|
|
63
|
+
else:
|
|
64
|
+
raise ValueError("Both `user` and `api_key` should not be `None`.")
|
|
65
|
+
|
|
66
|
+
if api_key is None:
|
|
67
|
+
if "@" in user: # type: ignore
|
|
68
|
+
email, handle = user, None
|
|
69
|
+
else:
|
|
70
|
+
email, handle = None, user
|
|
71
|
+
user_settings = load_user(email, handle)
|
|
72
|
+
|
|
73
|
+
if key is not None:
|
|
74
|
+
# within UserSettings, we still call it "password" for a while
|
|
75
|
+
user_settings.password = key
|
|
76
|
+
|
|
77
|
+
if user_settings.email is None:
|
|
78
|
+
raise SystemExit(f"✗ No stored user email, please call: lamin login {user}")
|
|
79
|
+
|
|
80
|
+
if user_settings.password is None:
|
|
81
|
+
raise SystemExit(
|
|
82
|
+
"✗ No stored API key, please call: lamin login <your-email> --key <API-key>"
|
|
83
|
+
)
|
|
61
84
|
else:
|
|
62
|
-
|
|
63
|
-
load_user(email, handle)
|
|
64
|
-
|
|
65
|
-
user_settings = load_or_create_user_settings()
|
|
66
|
-
|
|
67
|
-
if password is not None:
|
|
68
|
-
logger.warning(
|
|
69
|
-
"please use --key instead of --password, "
|
|
70
|
-
"passwords are deprecated and replaced with API keys"
|
|
71
|
-
)
|
|
72
|
-
key = password
|
|
73
|
-
|
|
74
|
-
if key is not None:
|
|
75
|
-
# within UserSettings, we still call it "password" for a while
|
|
76
|
-
user_settings.password = key
|
|
85
|
+
user_settings = load_or_create_user_settings()
|
|
77
86
|
|
|
78
|
-
|
|
79
|
-
raise RuntimeError("No stored user email, please call: lamin login {user}")
|
|
87
|
+
from .core._hub_core import sign_in_hub, sign_in_hub_api_key
|
|
80
88
|
|
|
81
|
-
if
|
|
82
|
-
|
|
83
|
-
|
|
89
|
+
if api_key is None:
|
|
90
|
+
response = sign_in_hub(
|
|
91
|
+
user_settings.email, # type: ignore
|
|
92
|
+
user_settings.password, # type: ignore
|
|
93
|
+
user_settings.handle,
|
|
84
94
|
)
|
|
95
|
+
else:
|
|
96
|
+
response = sign_in_hub_api_key(api_key)
|
|
85
97
|
|
|
86
|
-
from .core._hub_core import sign_in_hub
|
|
87
|
-
|
|
88
|
-
response = sign_in_hub(
|
|
89
|
-
user_settings.email, user_settings.password, user_settings.handle
|
|
90
|
-
)
|
|
91
98
|
if isinstance(response, Exception):
|
|
92
99
|
raise response
|
|
100
|
+
elif isinstance(response, str):
|
|
101
|
+
raise SystemExit(f"✗ Unsuccessful login: {response}.")
|
|
93
102
|
else:
|
|
94
103
|
user_uuid, user_id, user_handle, user_name, access_token = response
|
|
95
|
-
if
|
|
104
|
+
if api_key is not None:
|
|
105
|
+
logger.success(
|
|
106
|
+
f"logged in with API key (handle: {user_handle}, uid: {user_id})"
|
|
107
|
+
)
|
|
108
|
+
elif handle is None:
|
|
96
109
|
logger.success(f"logged in with handle {user_handle} (uid: {user_id})")
|
|
97
110
|
else:
|
|
98
111
|
logger.success(f"logged in with email {user_settings.email} (uid: {user_id})")
|
|
@@ -101,6 +114,7 @@ def login(
|
|
|
101
114
|
user_settings.name = user_name
|
|
102
115
|
user_settings._uuid = user_uuid
|
|
103
116
|
user_settings.access_token = access_token
|
|
117
|
+
user_settings.api_key = api_key
|
|
104
118
|
save_user_settings(user_settings)
|
|
105
119
|
|
|
106
120
|
if settings._instance_exists and _check_instance_setup():
|
|
@@ -113,6 +127,8 @@ def login(
|
|
|
113
127
|
def logout():
|
|
114
128
|
if current_user_settings_file().exists():
|
|
115
129
|
current_user_settings_file().unlink()
|
|
130
|
+
# update user info
|
|
131
|
+
settings._user_settings = None
|
|
116
132
|
logger.success("logged out")
|
|
117
133
|
else:
|
|
118
134
|
logger.important("already logged out")
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import json
|
|
3
4
|
import os
|
|
4
5
|
from urllib.request import urlretrieve
|
|
5
6
|
|
|
@@ -78,7 +79,7 @@ def connect_hub_with_auth(
|
|
|
78
79
|
|
|
79
80
|
if renew_token:
|
|
80
81
|
settings.user.access_token = get_access_token(
|
|
81
|
-
settings.user.email, settings.user.password
|
|
82
|
+
settings.user.email, settings.user.password, settings.user.api_key
|
|
82
83
|
)
|
|
83
84
|
access_token = settings.user.access_token
|
|
84
85
|
hub.postgrest.auth(access_token)
|
|
@@ -87,9 +88,17 @@ def connect_hub_with_auth(
|
|
|
87
88
|
|
|
88
89
|
|
|
89
90
|
# runs ~0.5s
|
|
90
|
-
def get_access_token(
|
|
91
|
+
def get_access_token(
|
|
92
|
+
email: str | None = None, password: str | None = None, api_key: str | None = None
|
|
93
|
+
):
|
|
91
94
|
hub = connect_hub()
|
|
92
95
|
try:
|
|
96
|
+
if api_key is not None:
|
|
97
|
+
auth_response = hub.functions.invoke(
|
|
98
|
+
"create-jwt",
|
|
99
|
+
invoke_options={"body": {"api_key": api_key}},
|
|
100
|
+
)
|
|
101
|
+
return json.loads(auth_response)["accessToken"]
|
|
93
102
|
auth_response = hub.auth.sign_in_with_password(
|
|
94
103
|
{
|
|
95
104
|
"email": email,
|
|
@@ -436,10 +436,11 @@ def _sign_in_hub(email: str, password: str, handle: str | None, client: Client):
|
|
|
436
436
|
)
|
|
437
437
|
data = client.table("account").select("*").eq("id", auth.user.id).execute().data
|
|
438
438
|
if data: # sync data from hub to local cache in case it was updated on the hub
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
439
|
+
user = data[0]
|
|
440
|
+
user_uuid = UUID(user["id"])
|
|
441
|
+
user_id = user["lnid"]
|
|
442
|
+
user_handle = user["handle"]
|
|
443
|
+
user_name = user["name"]
|
|
443
444
|
if handle is not None and handle != user_handle:
|
|
444
445
|
logger.warning(
|
|
445
446
|
f"using account handle {user_handle} (cached handle was {handle})"
|
|
@@ -458,7 +459,7 @@ def _sign_in_hub(email: str, password: str, handle: str | None, client: Client):
|
|
|
458
459
|
|
|
459
460
|
def sign_in_hub(
|
|
460
461
|
email: str, password: str, handle: str | None = None
|
|
461
|
-
) -> Exception | tuple[UUID, str, str, str, str]:
|
|
462
|
+
) -> Exception | str | tuple[UUID, str, str, str, str]:
|
|
462
463
|
try:
|
|
463
464
|
result = call_with_fallback(
|
|
464
465
|
_sign_in_hub, email=email, password=password, handle=handle
|
|
@@ -471,3 +472,53 @@ def sign_in_hub(
|
|
|
471
472
|
)
|
|
472
473
|
return exception
|
|
473
474
|
return result
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def _sign_in_hub_api_key(api_key: str, client: Client):
|
|
478
|
+
response = client.functions.invoke(
|
|
479
|
+
"create-jwt",
|
|
480
|
+
invoke_options={"body": {"api_key": api_key}},
|
|
481
|
+
)
|
|
482
|
+
access_token = json.loads(response)["accessToken"]
|
|
483
|
+
# probably need more info here to avoid additional queries
|
|
484
|
+
# like handle, uid etc
|
|
485
|
+
account_id = client.auth._decode_jwt(access_token)["sub"]
|
|
486
|
+
client.postgrest.auth(access_token)
|
|
487
|
+
# normally public.account.id is equal to auth.user.id
|
|
488
|
+
data = client.table("account").select("*").eq("id", account_id).execute().data
|
|
489
|
+
if data:
|
|
490
|
+
user = data[0]
|
|
491
|
+
user_uuid = UUID(user["id"])
|
|
492
|
+
user_id = user["lnid"]
|
|
493
|
+
user_handle = user["handle"]
|
|
494
|
+
user_name = user["name"]
|
|
495
|
+
else:
|
|
496
|
+
logger.error("Invalid API key.")
|
|
497
|
+
return "invalid-api-key"
|
|
498
|
+
return (user_uuid, user_id, user_handle, user_name, access_token)
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
def sign_in_hub_api_key(
|
|
502
|
+
api_key: str,
|
|
503
|
+
) -> Exception | str | tuple[UUID, str, str, str, str]:
|
|
504
|
+
try:
|
|
505
|
+
result = call_with_fallback(_sign_in_hub_api_key, api_key=api_key)
|
|
506
|
+
except Exception as exception:
|
|
507
|
+
logger.error(exception)
|
|
508
|
+
logger.error("Could not login. Probably your API key is wrong.")
|
|
509
|
+
return exception
|
|
510
|
+
return result
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def _create_api_key(body: dict, client: Client) -> str:
|
|
514
|
+
response = client.functions.invoke(
|
|
515
|
+
"create-api-key",
|
|
516
|
+
invoke_options={"body": body},
|
|
517
|
+
)
|
|
518
|
+
api_key = json.loads(response)["apiKey"]
|
|
519
|
+
return api_key
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
def create_api_key(body: dict) -> str:
|
|
523
|
+
api_key = call_with_fallback_auth(_create_api_key, body=body)
|
|
524
|
+
return api_key
|
|
@@ -71,30 +71,35 @@ def load_user_settings(user_settings_file: Path):
|
|
|
71
71
|
return settings
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
def _null_to_value(field, value=None):
|
|
75
|
+
return field if field != "null" else value
|
|
76
|
+
|
|
77
|
+
|
|
74
78
|
def setup_instance_from_store(store: InstanceSettingsStore) -> InstanceSettings:
|
|
75
79
|
ssettings = StorageSettings(
|
|
76
80
|
root=store.storage_root,
|
|
77
|
-
region=store.storage_region
|
|
81
|
+
region=_null_to_value(store.storage_region),
|
|
78
82
|
)
|
|
79
83
|
return InstanceSettings(
|
|
80
84
|
id=UUID(store.id),
|
|
81
85
|
owner=store.owner,
|
|
82
86
|
name=store.name,
|
|
83
87
|
storage=ssettings,
|
|
84
|
-
db=store.db
|
|
85
|
-
schema=store.schema_str
|
|
86
|
-
git_repo=store.git_repo
|
|
88
|
+
db=_null_to_value(store.db),
|
|
89
|
+
schema=_null_to_value(store.schema_str),
|
|
90
|
+
git_repo=_null_to_value(store.git_repo),
|
|
87
91
|
keep_artifacts_local=store.keep_artifacts_local, # type: ignore
|
|
88
92
|
)
|
|
89
93
|
|
|
90
94
|
|
|
91
95
|
def setup_user_from_store(store: UserSettingsStore) -> UserSettings:
|
|
92
96
|
settings = UserSettings()
|
|
93
|
-
settings.email = store.email
|
|
94
|
-
settings.password = store.password
|
|
97
|
+
settings.email = _null_to_value(store.email)
|
|
98
|
+
settings.password = _null_to_value(store.password)
|
|
95
99
|
settings.access_token = store.access_token
|
|
100
|
+
settings.api_key = _null_to_value(store.api_key)
|
|
96
101
|
settings.uid = store.uid
|
|
97
|
-
settings.handle = store.handle
|
|
98
|
-
settings.name = store.name
|
|
102
|
+
settings.handle = _null_to_value(store.handle, value="anonymous")
|
|
103
|
+
settings.name = _null_to_value(store.name)
|
|
99
104
|
settings._uuid = UUID(store.uuid) if store.uuid != "null" else None
|
|
100
105
|
return settings
|
|
@@ -21,8 +21,10 @@ class UserSettings:
|
|
|
21
21
|
|
|
22
22
|
handle: str = "anonymous"
|
|
23
23
|
"""Unique handle."""
|
|
24
|
-
email: str
|
|
24
|
+
email: str | None = None
|
|
25
25
|
"""User email."""
|
|
26
|
+
api_key: str | None = None
|
|
27
|
+
"""Beta API key."""
|
|
26
28
|
password: str | None = None
|
|
27
29
|
"""API key or legacy password."""
|
|
28
30
|
access_token: str | None = None
|
|
@@ -27,7 +27,7 @@ def lint(session: nox.Session) -> None:
|
|
|
27
27
|
["hub-local", "hub-prod", "hub-cloud", "storage", "docs"],
|
|
28
28
|
)
|
|
29
29
|
def install(session: nox.Session, group: str) -> None:
|
|
30
|
-
no_deps_packages = "git+https://github.com/laminlabs/lnschema-core git+https://github.com/laminlabs/wetlab lamin-cli"
|
|
30
|
+
no_deps_packages = "git+https://github.com/laminlabs/lnschema-core git+https://github.com/laminlabs/wetlab git+https://github.com/laminlabs/lamin-cli"
|
|
31
31
|
schema_deps = f"""uv pip install --system git+https://github.com/laminlabs/bionty git+https://github.com/laminlabs/lamindb@main
|
|
32
32
|
uv pip install --system --no-deps {no_deps_packages}
|
|
33
33
|
"""
|
|
@@ -18,6 +18,15 @@ from postgrest.exceptions import APIError
|
|
|
18
18
|
# ln_setup.delete("load_remote_instance", force=True)
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
# do not call hub if the owner is set to anonymous
|
|
22
|
+
def test_connect_anonymous_owned_instance_from_hub():
|
|
23
|
+
with pytest.raises(InstanceNotFoundError) as error:
|
|
24
|
+
ln_setup.connect("anonymous/random-instance-not-exists")
|
|
25
|
+
assert error.exconly().endswith(
|
|
26
|
+
"It is not possible to load an anonymous-owned instance from the hub"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
|
|
21
30
|
def test_connect_after_revoked_access():
|
|
22
31
|
# can't currently test this on staging as I'm missing the accounts
|
|
23
32
|
if os.getenv("LAMIN_ENV") == "prod":
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from datetime import datetime, timedelta, timezone
|
|
6
|
+
|
|
7
|
+
import lamindb_setup as ln_setup
|
|
8
|
+
import pytest
|
|
9
|
+
from lamindb_setup.core._hub_client import connect_hub_with_auth
|
|
10
|
+
from lamindb_setup.core._hub_core import create_api_key
|
|
11
|
+
from supafunc.errors import FunctionsHttpError
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_login():
|
|
15
|
+
ln_setup.login("testuser1")
|
|
16
|
+
assert ln_setup.settings.user.email == "testuser1@lamin.ai"
|
|
17
|
+
assert ln_setup.settings.user.uid == "DzTjkKse"
|
|
18
|
+
assert ln_setup.settings.user.handle == "testuser1"
|
|
19
|
+
|
|
20
|
+
import jwt
|
|
21
|
+
|
|
22
|
+
access_token_payload = jwt.decode(
|
|
23
|
+
ln_setup.settings.user.access_token,
|
|
24
|
+
algorithms="HS256",
|
|
25
|
+
options={"verify_signature": False},
|
|
26
|
+
)
|
|
27
|
+
assert access_token_payload["email"] == "testuser1@lamin.ai"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_login_api_key():
|
|
31
|
+
ln_setup.login("testuser1")
|
|
32
|
+
# obtain API key
|
|
33
|
+
expires_at = (datetime.now(tz=timezone.utc) + timedelta(days=1)).strftime(
|
|
34
|
+
"%Y-%m-%d"
|
|
35
|
+
)
|
|
36
|
+
api_key = create_api_key(
|
|
37
|
+
{"expires_at": expires_at, "description": "test_login_api_key"}
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
ln_setup.logout()
|
|
41
|
+
assert ln_setup.settings.user.handle == "anonymous"
|
|
42
|
+
|
|
43
|
+
with pytest.raises(FunctionsHttpError):
|
|
44
|
+
ln_setup.login(api_key="invalid-key")
|
|
45
|
+
|
|
46
|
+
with pytest.raises(ValueError):
|
|
47
|
+
ln_setup.login(user=None, api_key=None)
|
|
48
|
+
|
|
49
|
+
os.environ["LAMIN_API_KEY"] = api_key
|
|
50
|
+
ln_setup.login()
|
|
51
|
+
assert ln_setup.settings.user.handle == "testuser1"
|
|
52
|
+
assert ln_setup.settings.user.api_key == api_key
|
|
53
|
+
|
|
54
|
+
ln_setup.logout()
|
|
55
|
+
|
|
56
|
+
ln_setup.login(api_key=api_key)
|
|
57
|
+
assert ln_setup.settings.user.handle == "testuser1"
|
|
58
|
+
assert ln_setup.settings.user.api_key == api_key
|
|
59
|
+
|
|
60
|
+
# clean up
|
|
61
|
+
# here checks also refreshing access token with api_key
|
|
62
|
+
hub = connect_hub_with_auth(renew_token=True)
|
|
63
|
+
hub.table("api_key").delete().eq("description", "test_login_api_key").execute()
|
|
64
|
+
hub.auth.sign_out({"scope": "local"})
|
|
65
|
+
|
|
66
|
+
# login back with email to populate all fields
|
|
67
|
+
ln_setup.login("testuser1@lamin.ai")
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import lamindb_setup as ln_setup
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_login():
|
|
7
|
-
ln_setup.login("testuser1")
|
|
8
|
-
assert ln_setup.settings.user.email == "testuser1@lamin.ai"
|
|
9
|
-
assert ln_setup.settings.user.uid == "DzTjkKse"
|
|
10
|
-
assert ln_setup.settings.user.handle == "testuser1"
|
|
11
|
-
|
|
12
|
-
import jwt
|
|
13
|
-
|
|
14
|
-
access_token_payload = jwt.decode(
|
|
15
|
-
ln_setup.settings.user.access_token,
|
|
16
|
-
algorithms="HS256",
|
|
17
|
-
options={"verify_signature": False},
|
|
18
|
-
)
|
|
19
|
-
assert access_token_payload["email"] == "testuser1@lamin.ai"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/02-connect-local-instance.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/docs/hub-cloud/06-connect-hosted-instance.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{lamindb_setup-0.76.8 → lamindb_setup-0.77.0}/tests/hub-prod/test_switch_and_fallback_env.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|