lamin_cli 1.7.2__tar.gz → 1.8.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.
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/PKG-INFO +1 -1
- lamin_cli-1.8.1/lamin_cli/__init__.py +18 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/lamin_cli/__main__.py +38 -62
- lamin_cli-1.8.1/lamin_cli/_delete.py +43 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/lamin_cli/_save.py +17 -13
- lamin_cli-1.8.1/lamin_cli/_settings.py +96 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/core/conftest.py +0 -1
- lamin_cli-1.7.2/tests/core/test_create_switch_delete_list.py → lamin_cli-1.8.1/tests/core/test_create_switch_delete_list_settings.py +41 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/core/test_save_annotate_files.py +2 -1
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/core/test_save_notebooks.py +4 -8
- lamin_cli-1.7.2/lamin_cli/__init__.py +0 -12
- lamin_cli-1.7.2/lamin_cli/_settings.py +0 -62
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/.github/workflows/build.yml +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/.github/workflows/doc-changes.yml +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/.gitignore +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/.pre-commit-config.yaml +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/LICENSE +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/README.md +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/lamin_cli/_annotate.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/lamin_cli/_cache.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/lamin_cli/_load.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/lamin_cli/_migration.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/lamin_cli/compute/__init__.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/lamin_cli/compute/modal.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/lamin_cli/urls.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/noxfile.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/pyproject.toml +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/core/test_load.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/core/test_login.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/core/test_migrate.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/core/test_multi_process.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/core/test_parse_uid_from_code.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/core/test_save_annotate_scripts.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/core/test_save_r_code.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/modal/test_modal.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/notebooks/not-initialized.ipynb +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/notebooks/with-title-and-initialized-consecutive.ipynb +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/notebooks/with-title-and-initialized-non-consecutive.ipynb +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/scripts/merely-import-lamindb.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/scripts/run-track-and-finish-sync-git.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/scripts/run-track-and-finish.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/scripts/run-track-with-params.py +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/scripts/run-track.R +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/scripts/run-track.qmd +0 -0
- {lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/scripts/testscript.py +0 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Lamin CLI.
|
|
2
|
+
|
|
3
|
+
This is the command line interface for interacting with LaminDB & LaminHub.
|
|
4
|
+
|
|
5
|
+
The interface is defined in `__main__.py`. The root API here is used by LaminR to replicate the CLI functionality.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "1.8.1"
|
|
9
|
+
|
|
10
|
+
from lamindb_setup import disconnect, logout
|
|
11
|
+
from lamindb_setup._connect_instance import _connect_cli as connect
|
|
12
|
+
from lamindb_setup._init_instance import init
|
|
13
|
+
from lamindb_setup._setup_user import login
|
|
14
|
+
|
|
15
|
+
from ._delete import delete
|
|
16
|
+
from ._save import save
|
|
17
|
+
|
|
18
|
+
__all__ = ["save", "init", "connect", "delete", "login", "disconnect"]
|
|
@@ -11,6 +11,7 @@ from importlib.metadata import PackageNotFoundError, version
|
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from typing import TYPE_CHECKING, Literal
|
|
13
13
|
|
|
14
|
+
import lamindb_setup as ln_setup
|
|
14
15
|
from lamin_utils import logger
|
|
15
16
|
from lamindb_setup._init_instance import (
|
|
16
17
|
DOC_DB,
|
|
@@ -19,6 +20,13 @@ from lamindb_setup._init_instance import (
|
|
|
19
20
|
DOC_STORAGE_ARG,
|
|
20
21
|
)
|
|
21
22
|
|
|
23
|
+
from lamin_cli import connect as connect_
|
|
24
|
+
from lamin_cli import disconnect as disconnect_
|
|
25
|
+
from lamin_cli import init as init_
|
|
26
|
+
from lamin_cli import login as login_
|
|
27
|
+
from lamin_cli import logout as logout_
|
|
28
|
+
from lamin_cli import save as save_
|
|
29
|
+
|
|
22
30
|
from .urls import decompose_url
|
|
23
31
|
|
|
24
32
|
if TYPE_CHECKING:
|
|
@@ -129,16 +137,12 @@ def login(user: str, key: str | None):
|
|
|
129
137
|
|
|
130
138
|
See also: Login in a Python session via {func}`~lamindb.setup.login`.
|
|
131
139
|
"""
|
|
132
|
-
from lamindb_setup._setup_user import login as login_
|
|
133
|
-
|
|
134
140
|
return login_(user, key=key)
|
|
135
141
|
|
|
136
142
|
|
|
137
143
|
@main.command()
|
|
138
144
|
def logout():
|
|
139
145
|
"""Log out of LaminHub."""
|
|
140
|
-
from lamindb_setup import logout as logout_
|
|
141
|
-
|
|
142
146
|
return logout_()
|
|
143
147
|
|
|
144
148
|
|
|
@@ -170,8 +174,6 @@ def init(
|
|
|
170
174
|
|
|
171
175
|
See also: Init in a Python session via {func}`~lamindb.setup.init`.
|
|
172
176
|
"""
|
|
173
|
-
from lamindb_setup._init_instance import init as init_
|
|
174
|
-
|
|
175
177
|
return init_(storage=storage, db=db, modules=modules, name=name)
|
|
176
178
|
|
|
177
179
|
|
|
@@ -188,8 +190,7 @@ def connect(instance: str):
|
|
|
188
190
|
|
|
189
191
|
See also: Connect in a Python session via {func}`~lamindb.connect`.
|
|
190
192
|
"""
|
|
191
|
-
|
|
192
|
-
return _connect_cli(instance)
|
|
193
|
+
return connect_(instance)
|
|
193
194
|
|
|
194
195
|
|
|
195
196
|
@main.command()
|
|
@@ -198,8 +199,6 @@ def disconnect():
|
|
|
198
199
|
|
|
199
200
|
See also: Disconnect in a Python session via {func}`~lamindb.setup.disconnect`.
|
|
200
201
|
"""
|
|
201
|
-
from lamindb_setup import disconnect as disconnect_
|
|
202
|
-
|
|
203
202
|
return disconnect_()
|
|
204
203
|
|
|
205
204
|
|
|
@@ -211,18 +210,22 @@ def disconnect():
|
|
|
211
210
|
def create(entity: Literal["branch"], name: str | None = None):
|
|
212
211
|
"""Create a record for an entity.
|
|
213
212
|
|
|
214
|
-
Currently only supports creating
|
|
213
|
+
Currently only supports creating branches and projects.
|
|
215
214
|
|
|
216
215
|
```
|
|
217
216
|
lamin create branch --name my_branch
|
|
217
|
+
lamin create project --name my_project
|
|
218
218
|
```
|
|
219
219
|
"""
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
from lamindb.models import Branch
|
|
220
|
+
from lamindb.models import Branch, Project
|
|
223
221
|
|
|
224
|
-
|
|
225
|
-
|
|
222
|
+
if entity == "branch":
|
|
223
|
+
record = Branch(name=name).save()
|
|
224
|
+
elif entity == "project":
|
|
225
|
+
record = Project(name=name).save()
|
|
226
|
+
else:
|
|
227
|
+
raise NotImplementedError(f"Creating {entity} is not implemented.")
|
|
228
|
+
logger.important(f"created {entity}: {record.name}")
|
|
226
229
|
|
|
227
230
|
|
|
228
231
|
# fmt: off
|
|
@@ -243,9 +246,9 @@ def list_(entity: Literal["branch"], name: str | None = None):
|
|
|
243
246
|
from lamindb.models import Branch, Space
|
|
244
247
|
|
|
245
248
|
if entity == "branch":
|
|
246
|
-
print(Branch.
|
|
249
|
+
print(Branch.to_dataframe())
|
|
247
250
|
else:
|
|
248
|
-
print(Space.
|
|
251
|
+
print(Space.to_dataframe())
|
|
249
252
|
|
|
250
253
|
|
|
251
254
|
# fmt: off
|
|
@@ -303,38 +306,9 @@ def delete(entity: str, name: str | None = None, uid: str | None = None, slug: s
|
|
|
303
306
|
lamin delete instance --slug account/name
|
|
304
307
|
```
|
|
305
308
|
"""
|
|
306
|
-
from
|
|
309
|
+
from lamin_cli._delete import delete as delete_
|
|
307
310
|
|
|
308
|
-
|
|
309
|
-
if entity.startswith("https://") and "lamin" in entity:
|
|
310
|
-
url = entity
|
|
311
|
-
instance, entity, uid = decompose_url(url)
|
|
312
|
-
connect(instance)
|
|
313
|
-
|
|
314
|
-
if entity == "branch":
|
|
315
|
-
assert name is not None, "You have to pass a name for deleting a branch."
|
|
316
|
-
from lamindb import Branch
|
|
317
|
-
|
|
318
|
-
Branch.get(name=name).delete()
|
|
319
|
-
elif entity == "artifact":
|
|
320
|
-
assert uid is not None, "You have to pass a uid for deleting an artifact."
|
|
321
|
-
from lamindb import Artifact
|
|
322
|
-
|
|
323
|
-
Artifact.get(uid).delete()
|
|
324
|
-
elif entity == "transform":
|
|
325
|
-
assert uid is not None, "You have to pass a uid for deleting an transform."
|
|
326
|
-
from lamindb import Transform
|
|
327
|
-
|
|
328
|
-
Transform.get(uid).delete()
|
|
329
|
-
elif entity == "collection":
|
|
330
|
-
assert uid is not None, "You have to pass a uid for deleting an collection."
|
|
331
|
-
from lamindb import Collection
|
|
332
|
-
|
|
333
|
-
Collection.get(uid).delete()
|
|
334
|
-
elif entity == "instance":
|
|
335
|
-
return delete(slug, force=force)
|
|
336
|
-
else: # backwars compatibility
|
|
337
|
-
return delete(entity, force=force)
|
|
311
|
+
return delete_(entity=entity, name=name, uid=uid, slug=slug, force=force)
|
|
338
312
|
|
|
339
313
|
|
|
340
314
|
@main.command()
|
|
@@ -350,9 +324,9 @@ def load(entity: str, uid: str | None = None, key: str | None = None, with_env:
|
|
|
350
324
|
Pass a URL, `artifact`, or `transform`. For example:
|
|
351
325
|
|
|
352
326
|
```
|
|
353
|
-
lamin load https://lamin.ai/account/instance/artifact/
|
|
327
|
+
lamin load https://lamin.ai/account/instance/artifact/e2G7k9EVul4JbfsE
|
|
354
328
|
lamin load artifact --key mydatasets/mytable.parquet
|
|
355
|
-
lamin load artifact --uid
|
|
329
|
+
lamin load artifact --uid e2G7k9EVul4JbfsE
|
|
356
330
|
lamin load transform --key analysis.ipynb
|
|
357
331
|
lamin load transform --uid Vul4JbfsEYAy5
|
|
358
332
|
lamin load transform --uid Vul4JbfsEYAy5 --with-env
|
|
@@ -370,10 +344,6 @@ def load(entity: str, uid: str | None = None, key: str | None = None, with_env:
|
|
|
370
344
|
|
|
371
345
|
|
|
372
346
|
def _describe(entity: str = "artifact", uid: str | None = None, key: str | None = None):
|
|
373
|
-
import lamindb_setup as ln_setup
|
|
374
|
-
|
|
375
|
-
from ._load import decompose_url
|
|
376
|
-
|
|
377
347
|
if entity.startswith("https://") and "lamin" in entity:
|
|
378
348
|
url = entity
|
|
379
349
|
instance, entity, uid = decompose_url(url)
|
|
@@ -449,20 +419,20 @@ def save(path: str, key: str, description: str, stem_uid: str, project: str, spa
|
|
|
449
419
|
other file types and folders as {class}`~lamindb.Artifact`. You can enforce saving a file as
|
|
450
420
|
an {class}`~lamindb.Artifact` by passing `--registry artifact`.
|
|
451
421
|
"""
|
|
452
|
-
from lamin_cli import save as save_
|
|
453
|
-
|
|
454
422
|
if save_(path=path, key=key, description=description, stem_uid=stem_uid, project=project, space=space, branch=branch, registry=registry) is not None:
|
|
455
423
|
sys.exit(1)
|
|
456
424
|
|
|
457
425
|
|
|
458
426
|
@main.command()
|
|
427
|
+
@click.argument("entity", type=str, default=None, required=False)
|
|
459
428
|
@click.option("--key", type=str, default=None, help="The key of an artifact or transform.")
|
|
460
429
|
@click.option("--uid", type=str, default=None, help="The uid of an artifact or transform.")
|
|
461
430
|
@click.option("--project", type=str, default=None, help="A valid project name or uid.")
|
|
462
431
|
@click.option("--features", multiple=True, help="Feature annotations. Supports: feature=value, feature=val1,val2, or feature=\"val1\",\"val2\"")
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
432
|
+
def annotate(entity: str | None, key: str, uid: str, project: str, features: tuple):
|
|
433
|
+
"""Annotate an artifact or transform.
|
|
434
|
+
|
|
435
|
+
Entity is either 'artifact' or 'transform'. If not passed, chooses based on key suffix.
|
|
466
436
|
|
|
467
437
|
You can annotate with projects and valid features & values. For example,
|
|
468
438
|
|
|
@@ -477,11 +447,17 @@ def annotate(key: str, uid: str, project: str, registry: str, features: tuple):
|
|
|
477
447
|
from lamin_cli._annotate import _parse_features_list
|
|
478
448
|
from lamin_cli._save import infer_registry_from_path
|
|
479
449
|
|
|
480
|
-
|
|
450
|
+
# once we enable passing the URL as entity, then we don't need to throw this error
|
|
451
|
+
if not ln.setup.settings._instance_exists:
|
|
452
|
+
raise click.ClickException("Not connected to an instance. Please run: lamin connect account/name")
|
|
453
|
+
|
|
454
|
+
if entity is None:
|
|
481
455
|
if key is not None:
|
|
482
456
|
registry = infer_registry_from_path(key)
|
|
483
457
|
else:
|
|
484
458
|
registry = "artifact"
|
|
459
|
+
else:
|
|
460
|
+
registry = entity
|
|
485
461
|
if registry == "artifact":
|
|
486
462
|
model = ln.Artifact
|
|
487
463
|
else:
|
|
@@ -491,7 +467,7 @@ def annotate(key: str, uid: str, project: str, registry: str, features: tuple):
|
|
|
491
467
|
if key is not None:
|
|
492
468
|
artifact = model.get(key=key)
|
|
493
469
|
elif uid is not None:
|
|
494
|
-
artifact = model.get(uid=uid
|
|
470
|
+
artifact = model.get(uid) # do not use uid=uid, because then no truncated uids would work
|
|
495
471
|
else:
|
|
496
472
|
raise ln.errors.InvalidArgument("Either --key or --uid must be provided")
|
|
497
473
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from lamindb_setup import connect
|
|
2
|
+
from lamindb_setup import delete as delete_instance
|
|
3
|
+
|
|
4
|
+
from .urls import decompose_url
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def delete(
|
|
8
|
+
entity: str,
|
|
9
|
+
name: str | None = None,
|
|
10
|
+
uid: str | None = None,
|
|
11
|
+
slug: str | None = None,
|
|
12
|
+
force: bool = False,
|
|
13
|
+
):
|
|
14
|
+
# TODO: refactor to abstract getting and deleting across entities
|
|
15
|
+
if entity.startswith("https://") and "lamin" in entity:
|
|
16
|
+
url = entity
|
|
17
|
+
instance, entity, uid = decompose_url(url)
|
|
18
|
+
connect(instance)
|
|
19
|
+
|
|
20
|
+
if entity == "branch":
|
|
21
|
+
assert name is not None, "You have to pass a name for deleting a branch."
|
|
22
|
+
from lamindb import Branch
|
|
23
|
+
|
|
24
|
+
Branch.get(name=name).delete()
|
|
25
|
+
elif entity == "artifact":
|
|
26
|
+
assert uid is not None, "You have to pass a uid for deleting an artifact."
|
|
27
|
+
from lamindb import Artifact
|
|
28
|
+
|
|
29
|
+
Artifact.get(uid).delete()
|
|
30
|
+
elif entity == "transform":
|
|
31
|
+
assert uid is not None, "You have to pass a uid for deleting an transform."
|
|
32
|
+
from lamindb import Transform
|
|
33
|
+
|
|
34
|
+
Transform.get(uid).delete()
|
|
35
|
+
elif entity == "collection":
|
|
36
|
+
assert uid is not None, "You have to pass a uid for deleting an collection."
|
|
37
|
+
from lamindb import Collection
|
|
38
|
+
|
|
39
|
+
Collection.get(uid).delete()
|
|
40
|
+
elif entity == "instance":
|
|
41
|
+
return delete_instance(slug, force=force)
|
|
42
|
+
else: # backwards compatibility
|
|
43
|
+
return delete_instance(entity, force=force)
|
|
@@ -73,13 +73,13 @@ def parse_title_r_notebook(content: str) -> str | None:
|
|
|
73
73
|
|
|
74
74
|
def save(
|
|
75
75
|
path: Path | str,
|
|
76
|
-
key: str | None,
|
|
77
|
-
description: str | None,
|
|
78
|
-
stem_uid: str | None,
|
|
79
|
-
project: str | None,
|
|
80
|
-
space: str | None,
|
|
81
|
-
branch: str | None,
|
|
82
|
-
registry: str | None,
|
|
76
|
+
key: str | None = None,
|
|
77
|
+
description: str | None = None,
|
|
78
|
+
stem_uid: str | None = None,
|
|
79
|
+
project: str | None = None,
|
|
80
|
+
space: str | None = None,
|
|
81
|
+
branch: str | None = None,
|
|
82
|
+
registry: str | None = None,
|
|
83
83
|
) -> str | None:
|
|
84
84
|
import lamindb as ln
|
|
85
85
|
from lamindb._finish import save_context_core
|
|
@@ -104,12 +104,14 @@ def save(
|
|
|
104
104
|
raise ln.errors.InvalidArgument(
|
|
105
105
|
f"Project '{project}' not found, either create it with `ln.Project(name='...').save()` or fix typos."
|
|
106
106
|
)
|
|
107
|
+
space_record = None
|
|
107
108
|
if space is not None:
|
|
108
109
|
space_record = ln.Space.filter(ln.Q(name=space) | ln.Q(uid=space)).one_or_none()
|
|
109
110
|
if space_record is None:
|
|
110
111
|
raise ln.errors.InvalidArgument(
|
|
111
112
|
f"Space '{space}' not found, either create it on LaminHub or fix typos."
|
|
112
113
|
)
|
|
114
|
+
branch_record = None
|
|
113
115
|
if branch is not None:
|
|
114
116
|
branch_record = ln.Branch.filter(
|
|
115
117
|
ln.Q(name=branch) | ln.Q(uid=branch)
|
|
@@ -141,12 +143,14 @@ def save(
|
|
|
141
143
|
logger.error("Please pass a key or description via --key or --description")
|
|
142
144
|
return "missing-key-or-description"
|
|
143
145
|
|
|
144
|
-
artifact = ln.Artifact(
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
146
|
+
artifact = ln.Artifact(
|
|
147
|
+
path,
|
|
148
|
+
key=key,
|
|
149
|
+
description=description,
|
|
150
|
+
revises=revises,
|
|
151
|
+
branch=branch_record,
|
|
152
|
+
space=space_record,
|
|
153
|
+
).save()
|
|
150
154
|
logger.important(f"saved: {artifact}")
|
|
151
155
|
logger.important(f"storage path: {artifact.path}")
|
|
152
156
|
if artifact.storage.type == "s3":
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
if os.environ.get("NO_RICH"):
|
|
7
|
+
import click as click
|
|
8
|
+
else:
|
|
9
|
+
import rich_click as click
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@click.group(invoke_without_command=True)
|
|
13
|
+
@click.pass_context
|
|
14
|
+
def settings(ctx):
|
|
15
|
+
"""Manage settings.
|
|
16
|
+
|
|
17
|
+
Call without subcommands and options to show settings:
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
lamin settings
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Allows to get and set these settings:
|
|
24
|
+
|
|
25
|
+
- `work-dir` → {attr}`~lamindb.setup.core.SetupSettings.work_dir`
|
|
26
|
+
- `private-django-api` → {attr}`~lamindb.setup.core.SetupSettings.private_django_api`
|
|
27
|
+
- `branch` → current branch (use `lamin switch --branch` to change)
|
|
28
|
+
- `space` → current space (use `lamin switch --space` to change)
|
|
29
|
+
|
|
30
|
+
Examples for getting a setting:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
lamin settings get work-dir
|
|
34
|
+
lamin settings get branch
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Examples for setting the working directory:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
lamin settings set work-dir . # set work-dir to current directory
|
|
41
|
+
lamin settings set work-dir ~/my-project # set work-dir to ~/my-project
|
|
42
|
+
lamin settings set work-dir none # unset work-dir
|
|
43
|
+
```
|
|
44
|
+
"""
|
|
45
|
+
if ctx.invoked_subcommand is None:
|
|
46
|
+
from lamindb_setup import settings as settings_
|
|
47
|
+
|
|
48
|
+
click.echo("Configure: see `lamin settings --help`")
|
|
49
|
+
click.echo(settings_)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@settings.command("set")
|
|
53
|
+
@click.argument(
|
|
54
|
+
"setting",
|
|
55
|
+
type=click.Choice(
|
|
56
|
+
["auto-connect", "private-django-api", "work-dir"], case_sensitive=False
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
@click.argument("value") # No explicit type - let Click handle it
|
|
60
|
+
def set(setting: str, value: str):
|
|
61
|
+
"""Set a setting."""
|
|
62
|
+
from lamindb_setup import settings as settings_
|
|
63
|
+
|
|
64
|
+
if setting == "auto-connect":
|
|
65
|
+
settings_.auto_connect = click.BOOL(value)
|
|
66
|
+
if setting == "private-django-api":
|
|
67
|
+
settings_.private_django_api = click.BOOL(value)
|
|
68
|
+
if setting == "work-dir":
|
|
69
|
+
if value.lower() == "none":
|
|
70
|
+
value = None # type: ignore[assignment]
|
|
71
|
+
settings_.work_dir = value
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@settings.command("get")
|
|
75
|
+
@click.argument(
|
|
76
|
+
"setting",
|
|
77
|
+
type=click.Choice(
|
|
78
|
+
["auto-connect", "private-django-api", "space", "branch", "work-dir"],
|
|
79
|
+
case_sensitive=False,
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
def get(setting: str):
|
|
83
|
+
"""Get a setting."""
|
|
84
|
+
from lamindb_setup import settings as settings_
|
|
85
|
+
|
|
86
|
+
if setting == "branch":
|
|
87
|
+
_, value = settings_._read_branch_idlike_name()
|
|
88
|
+
elif setting == "space":
|
|
89
|
+
_, value = settings_._read_space_idlike_name()
|
|
90
|
+
elif setting == "work-dir":
|
|
91
|
+
value = settings_.work_dir
|
|
92
|
+
if value is None:
|
|
93
|
+
value = "None"
|
|
94
|
+
else:
|
|
95
|
+
value = getattr(settings_, setting.replace("-", "_"))
|
|
96
|
+
click.echo(value)
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
import lamindb as ln
|
|
5
6
|
import lamindb_setup as ln_setup
|
|
6
7
|
|
|
7
8
|
|
|
9
|
+
def test_create_project():
|
|
10
|
+
exit_status = os.system("lamin create project --name testproject")
|
|
11
|
+
assert exit_status == 0
|
|
12
|
+
|
|
13
|
+
|
|
8
14
|
def test_branch():
|
|
9
15
|
exit_status = os.system("lamin switch --branch archive")
|
|
10
16
|
assert exit_status == 0
|
|
@@ -40,3 +46,38 @@ def test_space():
|
|
|
40
46
|
exit_status = os.system("lamin switch --space all")
|
|
41
47
|
assert exit_status == 0
|
|
42
48
|
assert ln_setup.settings.space.uid == 12 * "a"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def test_work_dir():
|
|
52
|
+
# default work-dir is None
|
|
53
|
+
result = subprocess.run(
|
|
54
|
+
"lamin settings get work-dir",
|
|
55
|
+
capture_output=True,
|
|
56
|
+
text=True,
|
|
57
|
+
shell=True,
|
|
58
|
+
)
|
|
59
|
+
assert result.stdout.strip() == "None"
|
|
60
|
+
assert ln_setup.settings.work_dir is None
|
|
61
|
+
# set work-dir to tmp_path
|
|
62
|
+
this_path = Path(__file__).resolve()
|
|
63
|
+
exit_status = os.system(f"lamin settings set work-dir {this_path.parent}")
|
|
64
|
+
assert exit_status == 0
|
|
65
|
+
result = subprocess.run(
|
|
66
|
+
"lamin settings get work-dir",
|
|
67
|
+
capture_output=True,
|
|
68
|
+
text=True,
|
|
69
|
+
shell=True,
|
|
70
|
+
)
|
|
71
|
+
assert result.stdout.strip() == str(this_path.parent)
|
|
72
|
+
assert ln_setup.settings.work_dir == this_path.parent
|
|
73
|
+
# unset work-dir
|
|
74
|
+
exit_status = os.system("lamin settings set work-dir none")
|
|
75
|
+
assert exit_status == 0
|
|
76
|
+
result = subprocess.run(
|
|
77
|
+
"lamin settings get work-dir",
|
|
78
|
+
capture_output=True,
|
|
79
|
+
text=True,
|
|
80
|
+
shell=True,
|
|
81
|
+
)
|
|
82
|
+
assert result.stdout.strip() == "None"
|
|
83
|
+
assert ln_setup.settings.work_dir is None
|
|
@@ -45,7 +45,8 @@ def test_save_and_annotate_local_file():
|
|
|
45
45
|
assert artifact.branch == branch
|
|
46
46
|
assert project in artifact.projects.all()
|
|
47
47
|
|
|
48
|
-
# test passing the registry and saving the same file
|
|
48
|
+
# test passing the registry and saving the same file on the main branch
|
|
49
|
+
# it should recognize the file on the contrib1 branch
|
|
49
50
|
result = subprocess.run(
|
|
50
51
|
f"lamin save {filepath} --key mytest --registry artifact",
|
|
51
52
|
shell=True,
|
|
@@ -94,10 +94,8 @@ def test_save_consecutive_user_passes_uid():
|
|
|
94
94
|
env=env,
|
|
95
95
|
)
|
|
96
96
|
assert result.returncode == 0
|
|
97
|
-
assert (
|
|
98
|
-
|
|
99
|
-
in notebook_path.read_text()
|
|
100
|
-
)
|
|
97
|
+
assert "loaded Transform('hlsFXswrJjtt0000'" in notebook_path.read_text()
|
|
98
|
+
assert "started new Run" in notebook_path.read_text()
|
|
101
99
|
|
|
102
100
|
# now let's simulate the interactive use case and use nbproject_test again
|
|
103
101
|
# use the stem uid instead and it will bump the version
|
|
@@ -107,10 +105,8 @@ def test_save_consecutive_user_passes_uid():
|
|
|
107
105
|
assert r"ln.track(\"hlsFXswrJjtt\")" in notebook_path.read_text()
|
|
108
106
|
|
|
109
107
|
nbproject_test.execute_notebooks(notebook_path, print_outputs=True)
|
|
110
|
-
assert (
|
|
111
|
-
|
|
112
|
-
in notebook_path.read_text()
|
|
113
|
-
)
|
|
108
|
+
assert "created Transform('hlsFXswrJjtt0001'" in notebook_path.read_text()
|
|
109
|
+
assert "started new Run" in notebook_path.read_text()
|
|
114
110
|
|
|
115
111
|
# now, there is a transform record, but we're missing all artifacts because ln.finish() wasn't called
|
|
116
112
|
transform = ln.Transform.filter(uid="hlsFXswrJjtt0001").one_or_none()
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
"""Lamin CLI.
|
|
2
|
-
|
|
3
|
-
This is the command line interface for interacting with LaminDB & LaminHub.
|
|
4
|
-
|
|
5
|
-
The interface is defined in `__main__.py`. The root API here is used by LaminR to replicate the CLI functionality.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
__version__ = "1.7.2"
|
|
9
|
-
|
|
10
|
-
from ._save import save
|
|
11
|
-
|
|
12
|
-
__all__ = ["save"]
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
|
-
if os.environ.get("NO_RICH"):
|
|
6
|
-
import click as click
|
|
7
|
-
else:
|
|
8
|
-
import rich_click as click
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
@click.group(invoke_without_command=True)
|
|
12
|
-
@click.pass_context
|
|
13
|
-
def settings(ctx):
|
|
14
|
-
"""Manage settings.
|
|
15
|
-
|
|
16
|
-
Call without subcommands and options to show settings.
|
|
17
|
-
"""
|
|
18
|
-
if ctx.invoked_subcommand is None:
|
|
19
|
-
from lamindb_setup import settings as settings_
|
|
20
|
-
|
|
21
|
-
click.echo("Configure: see `lamin settings --help`")
|
|
22
|
-
click.echo(settings_)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@settings.command("set")
|
|
26
|
-
@click.argument(
|
|
27
|
-
"setting",
|
|
28
|
-
type=click.Choice(["auto-connect", "private-django-api"], case_sensitive=False),
|
|
29
|
-
)
|
|
30
|
-
@click.argument("value", type=click.BOOL)
|
|
31
|
-
def set(setting: str, value: bool):
|
|
32
|
-
"""Update settings.
|
|
33
|
-
|
|
34
|
-
- `auto-connect` → {attr}`~lamindb.setup.core.SetupSettings.auto_connect`
|
|
35
|
-
- `private-django-api` → {attr}`~lamindb.setup.core.SetupSettings.private_django_api`
|
|
36
|
-
"""
|
|
37
|
-
from lamindb_setup import settings as settings_
|
|
38
|
-
|
|
39
|
-
if setting == "auto-connect":
|
|
40
|
-
settings_.auto_connect = value
|
|
41
|
-
if setting == "private-django-api":
|
|
42
|
-
settings_.private_django_api = value
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@settings.command("get")
|
|
46
|
-
@click.argument(
|
|
47
|
-
"setting",
|
|
48
|
-
type=click.Choice(
|
|
49
|
-
["auto-connect", "private-django-api", "space", "branch"], case_sensitive=False
|
|
50
|
-
),
|
|
51
|
-
)
|
|
52
|
-
def get(setting: str):
|
|
53
|
-
"""Get a setting."""
|
|
54
|
-
from lamindb_setup import settings as settings_
|
|
55
|
-
|
|
56
|
-
if setting == "branch":
|
|
57
|
-
_, value = settings_._read_branch_idlike_name()
|
|
58
|
-
elif setting == "space":
|
|
59
|
-
_, value = settings_._read_space_idlike_name()
|
|
60
|
-
else:
|
|
61
|
-
value = getattr(settings_, setting.replace("-", "_"))
|
|
62
|
-
click.echo(value)
|
|
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
|
{lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/notebooks/with-title-and-initialized-consecutive.ipynb
RENAMED
|
File without changes
|
{lamin_cli-1.7.2 → lamin_cli-1.8.1}/tests/notebooks/with-title-and-initialized-non-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
|