lamin_cli 0.17.2__tar.gz → 0.17.3__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.
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/PKG-INFO +1 -1
- lamin_cli-0.17.3/lamin_cli/__init__.py +3 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/lamin_cli/__main__.py +52 -56
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/lamin_cli/_get.py +17 -1
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/test_cli.py +22 -4
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/test_get.py +2 -1
- lamin_cli-0.17.2/lamin_cli/__init__.py +0 -3
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/.github/workflows/doc-changes.yml +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/.gitignore +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/.pre-commit-config.yaml +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/LICENSE +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/README.md +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/lamin_cli/_cache.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/lamin_cli/_migration.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/lamin_cli/_save.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/pyproject.toml +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/conftest.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/notebooks/not-initialized.ipynb +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/notebooks/with-title-and-initialized-consecutive.ipynb +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/notebooks/with-title-and-initialized-non-consecutive.ipynb +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/scripts/merely-import-lamindb.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/scripts/run-track-and-finish-sync-git.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/scripts/run-track-and-finish.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/test_migrate.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/test_multi_process.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/test_save_files.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/test_save_notebooks.py +0 -0
- {lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/test_save_scripts.py +0 -0
|
@@ -41,9 +41,7 @@ else:
|
|
|
41
41
|
"init",
|
|
42
42
|
"load",
|
|
43
43
|
"info",
|
|
44
|
-
"close",
|
|
45
44
|
"delete",
|
|
46
|
-
"logout",
|
|
47
45
|
],
|
|
48
46
|
},
|
|
49
47
|
{
|
|
@@ -55,8 +53,8 @@ else:
|
|
|
55
53
|
"commands": ["cache", "set"],
|
|
56
54
|
},
|
|
57
55
|
{
|
|
58
|
-
"name": "Schema
|
|
59
|
-
"commands": ["migrate"
|
|
56
|
+
"name": "Schema migration",
|
|
57
|
+
"commands": ["migrate"],
|
|
60
58
|
},
|
|
61
59
|
]
|
|
62
60
|
}
|
|
@@ -98,7 +96,8 @@ def main():
|
|
|
98
96
|
@main.command()
|
|
99
97
|
@click.argument("user", type=str, default=None, required=False)
|
|
100
98
|
@click.option("--key", type=str, default=None, help="The API key.")
|
|
101
|
-
|
|
99
|
+
@click.option("--logout", is_flag=True, help="Logout instead of logging in.")
|
|
100
|
+
def login(user: str, key: Optional[str], logout: bool = False):
|
|
102
101
|
"""Log into LaminHub.
|
|
103
102
|
|
|
104
103
|
Upon logging in the first time, you need to pass your API key via:
|
|
@@ -119,17 +118,22 @@ def login(user: str, key: Optional[str]):
|
|
|
119
118
|
|
|
120
119
|
You will be prompted for your Beta API key unless you set an environment variable `LAMIN_API_KEY`.
|
|
121
120
|
"""
|
|
122
|
-
|
|
121
|
+
if logout:
|
|
122
|
+
from lamindb_setup._setup_user import logout as logout_func
|
|
123
123
|
|
|
124
|
-
|
|
125
|
-
if "LAMIN_API_KEY" in os.environ:
|
|
126
|
-
api_key = os.environ["LAMIN_API_KEY"]
|
|
127
|
-
else:
|
|
128
|
-
api_key = input("Your API key: ")
|
|
124
|
+
return logout_func()
|
|
129
125
|
else:
|
|
130
|
-
|
|
126
|
+
from lamindb_setup._setup_user import login
|
|
127
|
+
|
|
128
|
+
if user is None:
|
|
129
|
+
if "LAMIN_API_KEY" in os.environ:
|
|
130
|
+
api_key = os.environ["LAMIN_API_KEY"]
|
|
131
|
+
else:
|
|
132
|
+
api_key = input("Your API key: ")
|
|
133
|
+
else:
|
|
134
|
+
api_key = None
|
|
131
135
|
|
|
132
|
-
|
|
136
|
+
return login(user, key=key, api_key=api_key)
|
|
133
137
|
|
|
134
138
|
|
|
135
139
|
# fmt: off
|
|
@@ -148,39 +152,45 @@ def init(storage: str, db: Optional[str], schema: Optional[str], name: Optional[
|
|
|
148
152
|
|
|
149
153
|
# fmt: off
|
|
150
154
|
@main.command()
|
|
151
|
-
@click.argument("
|
|
155
|
+
@click.argument("instance", type=str, default=None)
|
|
152
156
|
@click.option("--db", type=str, default=None, help="Update database URL.") # noqa: E501
|
|
153
157
|
@click.option("--storage", type=str, default=None, help="Update storage while loading.")
|
|
158
|
+
@click.option("--unload", is_flag=True, help="Unload the current instance.")
|
|
154
159
|
# fmt: on
|
|
155
|
-
def load(
|
|
160
|
+
def load(
|
|
161
|
+
instance: Optional[str], db: Optional[str], storage: Optional[str], unload: bool
|
|
162
|
+
):
|
|
156
163
|
"""Load an instance for auto-connection.
|
|
157
164
|
|
|
158
|
-
|
|
159
|
-
(`https://lamin.ai/account/
|
|
165
|
+
Pass a slug (`account/name`) or URL
|
|
166
|
+
(`https://lamin.ai/account/name`).
|
|
160
167
|
"""
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
settings.auto_connect = True
|
|
164
|
-
return connect(slug=identifier, db=db, storage=storage)
|
|
165
|
-
|
|
168
|
+
if unload:
|
|
169
|
+
from lamindb_setup._close import close as close_
|
|
166
170
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
+
return close_()
|
|
172
|
+
else:
|
|
173
|
+
if instance is None:
|
|
174
|
+
raise click.UsageError("INSTANCE is required when loading an instance.")
|
|
175
|
+
from lamindb_setup import settings, connect
|
|
171
176
|
|
|
172
|
-
|
|
177
|
+
settings.auto_connect = True
|
|
178
|
+
return connect(slug=instance, db=db, storage=storage)
|
|
173
179
|
|
|
174
180
|
|
|
175
181
|
@main.command()
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
@click.option("--schema", is_flag=True, help="View schema.")
|
|
183
|
+
def info(schema: bool):
|
|
184
|
+
"""Show info about current instance."""
|
|
185
|
+
if schema:
|
|
186
|
+
from lamindb_setup._schema import view
|
|
178
187
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
188
|
+
print("Open in browser: http://127.0.0.1:8000/schema/")
|
|
189
|
+
return view()
|
|
190
|
+
else:
|
|
191
|
+
import lamindb_setup
|
|
182
192
|
|
|
183
|
-
|
|
193
|
+
print(lamindb_setup.settings)
|
|
184
194
|
|
|
185
195
|
|
|
186
196
|
# fmt: off
|
|
@@ -195,22 +205,17 @@ def delete(instance: str, force: bool = False):
|
|
|
195
205
|
return delete(instance, force=force)
|
|
196
206
|
|
|
197
207
|
|
|
198
|
-
@main.command()
|
|
199
|
-
def logout():
|
|
200
|
-
"""Logout."""
|
|
201
|
-
from lamindb_setup._setup_user import logout
|
|
202
|
-
|
|
203
|
-
return logout()
|
|
204
|
-
|
|
205
|
-
|
|
206
208
|
@main.command()
|
|
207
209
|
@click.argument("entity", type=str)
|
|
208
|
-
@click.option("--uid", help="
|
|
209
|
-
@click.option("--key", help="The key for the entity")
|
|
210
|
-
|
|
210
|
+
@click.option("--uid", help="The uid for the entity.")
|
|
211
|
+
@click.option("--key", help="The key for the entity.")
|
|
212
|
+
@click.option(
|
|
213
|
+
"--with-env", is_flag=True, help="Also return the environment for a tranform."
|
|
214
|
+
)
|
|
215
|
+
def get(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
211
216
|
"""Query an entity.
|
|
212
217
|
|
|
213
|
-
Pass a
|
|
218
|
+
Pass a URL, `artifact`, or `transform`. For example:
|
|
214
219
|
|
|
215
220
|
```
|
|
216
221
|
lamin get https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsEYAy5
|
|
@@ -218,11 +223,12 @@ def get(entity: str, uid: str = None, key: str = None):
|
|
|
218
223
|
lamin get artifact --uid e2G7k9EVul4JbfsEYAy5
|
|
219
224
|
lamin get transform --key analysis.ipynb
|
|
220
225
|
lamin get transform --uid Vul4JbfsEYAy5
|
|
226
|
+
lamin get transform --uid Vul4JbfsEYAy5 --with-env
|
|
221
227
|
```
|
|
222
228
|
"""
|
|
223
229
|
from lamin_cli._get import get
|
|
224
230
|
|
|
225
|
-
return get(entity, uid, key)
|
|
231
|
+
return get(entity, uid=uid, key=key, with_env=with_env)
|
|
226
232
|
|
|
227
233
|
|
|
228
234
|
@main.command()
|
|
@@ -265,16 +271,6 @@ def set_(setting: str, value: bool):
|
|
|
265
271
|
main.add_command(migrate)
|
|
266
272
|
|
|
267
273
|
|
|
268
|
-
@main.command()
|
|
269
|
-
@click.argument("action", type=click.Choice(["view"]))
|
|
270
|
-
def schema(action: str):
|
|
271
|
-
"""View schema."""
|
|
272
|
-
from lamindb_setup._schema import view
|
|
273
|
-
|
|
274
|
-
if action == "view":
|
|
275
|
-
return view()
|
|
276
|
-
|
|
277
|
-
|
|
278
274
|
# https://stackoverflow.com/questions/57810659/automatically-generate-all-help-documentation-for-click-commands
|
|
279
275
|
# https://claude.ai/chat/73c28487-bec3-4073-8110-50d1a2dd6b84
|
|
280
276
|
def _generate_help():
|
|
@@ -15,7 +15,7 @@ def decompose_url(url: str) -> Tuple[str, str, str]:
|
|
|
15
15
|
return instance_slug, entity, uid
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def get(entity: str, uid: str = None, key: str = None):
|
|
18
|
+
def get(entity: str, uid: str = None, key: str = None, with_env: bool = False):
|
|
19
19
|
if entity.startswith("https://lamin.ai"):
|
|
20
20
|
url = entity
|
|
21
21
|
instance_slug, entity, uid = decompose_url(url)
|
|
@@ -41,6 +41,9 @@ def get(entity: str, uid: str = None, key: str = None):
|
|
|
41
41
|
import lamindb as ln
|
|
42
42
|
from lamindb._finish import script_to_notebook
|
|
43
43
|
|
|
44
|
+
# below is to silence warnings about missing run inputs
|
|
45
|
+
ln.settings.track_run_inputs = False
|
|
46
|
+
|
|
44
47
|
if entity == "transform":
|
|
45
48
|
transform = (
|
|
46
49
|
ln.Transform.get(uid) if uid is not None else ln.Transform.get(key=key)
|
|
@@ -60,6 +63,19 @@ def get(entity: str, uid: str = None, key: str = None):
|
|
|
60
63
|
else:
|
|
61
64
|
raise ValueError("No source code available for this transform.")
|
|
62
65
|
logger.important(target_filename)
|
|
66
|
+
if with_env:
|
|
67
|
+
if (
|
|
68
|
+
transform.latest_run is not None
|
|
69
|
+
and transform.latest_run.environment is not None
|
|
70
|
+
):
|
|
71
|
+
filepath_env_cache = transform.latest_run.environment.cache()
|
|
72
|
+
target_env_filename = (
|
|
73
|
+
".".join(target_filename.split(".")[:-1]) + "__requirements.txt"
|
|
74
|
+
)
|
|
75
|
+
filepath_env_cache.rename(target_env_filename)
|
|
76
|
+
logger.important(target_env_filename)
|
|
77
|
+
else:
|
|
78
|
+
logger.warning("latest transform run with environment doesn't exist")
|
|
63
79
|
elif entity == "artifact":
|
|
64
80
|
artifact = ln.Artifact.get(uid) if uid is not None else ln.Artifact.get(key=key)
|
|
65
81
|
cache_path = artifact.cache()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from datetime import datetime, timedelta, timezone
|
|
3
|
+
from lamindb_setup import settings
|
|
3
4
|
from lamindb_setup.core._hub_client import connect_hub_with_auth
|
|
4
5
|
from lamindb_setup.core._hub_core import create_api_key
|
|
5
6
|
|
|
@@ -13,13 +14,20 @@ def test_cli_login():
|
|
|
13
14
|
exit_status = os.system("lamin login testuser1")
|
|
14
15
|
assert exit_status == 0
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
assert settings.user.handle == "testuser1"
|
|
18
|
+
password = settings.user.password
|
|
19
|
+
assert password is not None
|
|
20
|
+
|
|
21
|
+
exit_status = os.system(f"lamin login testuser1 --key {password}")
|
|
19
22
|
assert exit_status == 0
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
def test_cli_login_api_key():
|
|
26
|
+
settings._user_settings = None # this is to refresh a settings instance
|
|
27
|
+
assert settings.user.handle == "testuser1"
|
|
28
|
+
password = settings.user.password
|
|
29
|
+
assert password is not None
|
|
30
|
+
|
|
23
31
|
expires_at = datetime.now(tz=timezone.utc) + timedelta(days=1)
|
|
24
32
|
api_key = create_api_key(
|
|
25
33
|
{
|
|
@@ -35,8 +43,18 @@ def test_cli_login_api_key():
|
|
|
35
43
|
exit_status = os.system("lamin login")
|
|
36
44
|
assert exit_status == 0
|
|
37
45
|
|
|
46
|
+
settings._user_settings = None
|
|
47
|
+
assert settings.user.handle == "testuser1"
|
|
48
|
+
assert settings.user.api_key == api_key
|
|
49
|
+
|
|
38
50
|
hub = connect_hub_with_auth()
|
|
39
51
|
hub.table("api_key").delete().eq("description", "test_cli_login_api_key").execute()
|
|
40
52
|
hub.auth.sign_out({"scope": "local"})
|
|
41
53
|
|
|
42
|
-
os.system("lamin login testuser1@lamin.ai")
|
|
54
|
+
exit_status = os.system(f"lamin login testuser1@lamin.ai --key {password}")
|
|
55
|
+
assert exit_status == 0
|
|
56
|
+
|
|
57
|
+
settings._user_settings = None
|
|
58
|
+
assert settings.user.handle == "testuser1"
|
|
59
|
+
assert settings.user.api_key is None
|
|
60
|
+
assert settings.user.password == password
|
|
@@ -14,7 +14,8 @@ def test_decompose_url():
|
|
|
14
14
|
def test_get_transform():
|
|
15
15
|
result = subprocess.run(
|
|
16
16
|
"lamin get"
|
|
17
|
-
" 'https://lamin.ai/laminlabs/arrayloader-benchmarks/transform/1GCKs8zLtkc85zKv'"
|
|
17
|
+
" 'https://lamin.ai/laminlabs/arrayloader-benchmarks/transform/1GCKs8zLtkc85zKv'"
|
|
18
|
+
" --with-env", # noqa
|
|
18
19
|
shell=True,
|
|
19
20
|
capture_output=True,
|
|
20
21
|
)
|
|
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
|
{lamin_cli-0.17.2 → lamin_cli-0.17.3}/tests/notebooks/with-title-and-initialized-consecutive.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
|