lamin_cli 1.10.0__tar.gz → 1.11.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.
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/PKG-INFO +2 -3
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/__init__.py +1 -1
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/__main__.py +39 -0
- lamin_cli-1.11.0/lamin_cli/_context.py +76 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/_io.py +3 -3
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/_load.py +6 -1
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/_save.py +18 -4
- lamin_cli-1.11.0/tests/core/test_save_shell_script.py +38 -0
- lamin_cli-1.11.0/tests/core/test_track.py +65 -0
- lamin_cli-1.11.0/tests/scripts/track-lineage.sh +15 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/.github/workflows/build.yml +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/.github/workflows/doc-changes.yml +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/.gitignore +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/.pre-commit-config.yaml +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/LICENSE +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/README.md +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/_annotate.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/_cache.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/_delete.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/_migration.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/_settings.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/clone/__init__.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/clone/_clone_verification.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/clone/create_sqlite_clone_and_import_db.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/compute/__init__.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/compute/modal.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/lamin_cli/urls.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/noxfile.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/pyproject.toml +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/conftest.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_create_switch_delete_list_settings.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_io.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_load.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_login.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_migrate.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_multi_process.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_parse_uid_from_code.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_save_annotate_files.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_save_annotate_scripts.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_save_notebooks.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/core/test_save_r_code.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/modal/test_modal.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/notebooks/not-initialized.ipynb +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/notebooks/with-title-and-initialized-consecutive.ipynb +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/notebooks/with-title-and-initialized-non-consecutive.ipynb +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/scripts/merely-import-lamindb.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/scripts/run-track-and-finish-sync-git.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/scripts/run-track-and-finish.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/scripts/run-track-with-params.py +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/scripts/run-track.R +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/scripts/run-track.qmd +0 -0
- {lamin_cli-1.10.0 → lamin_cli-1.11.0}/tests/scripts/testscript.py +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: lamin_cli
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.11.0
|
|
4
4
|
Summary: Lamin CLI.
|
|
5
5
|
Author-email: Lamin Labs <open-source@lamin.ai>
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
7
|
-
License-File: LICENSE
|
|
8
7
|
Requires-Dist: rich-click>=1.7
|
|
9
8
|
Project-URL: Home, https://github.com/laminlabs/lamin-cli
|
|
10
9
|
|
|
@@ -6,7 +6,7 @@ The interface is defined in `__main__.py`.
|
|
|
6
6
|
The root API here is used by LaminR to replicate the CLI functionality.
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
__version__ = "1.
|
|
9
|
+
__version__ = "1.11.0"
|
|
10
10
|
|
|
11
11
|
from lamindb_setup import disconnect, logout
|
|
12
12
|
from lamindb_setup._connect_instance import _connect_cli as connect
|
|
@@ -42,6 +42,10 @@ COMMAND_GROUPS = {
|
|
|
42
42
|
"name": "Load, save, create & delete data",
|
|
43
43
|
"commands": ["load", "save", "create", "delete"],
|
|
44
44
|
},
|
|
45
|
+
{
|
|
46
|
+
"name": "Tracking within shell scripts",
|
|
47
|
+
"commands": ["track", "finish"],
|
|
48
|
+
},
|
|
45
49
|
{
|
|
46
50
|
"name": "Describe, annotate & list data",
|
|
47
51
|
"commands": ["describe", "annotate", "list"],
|
|
@@ -436,6 +440,41 @@ def save(path: str, key: str, description: str, stem_uid: str, project: str, spa
|
|
|
436
440
|
if save_(path=path, key=key, description=description, stem_uid=stem_uid, project=project, space=space, branch=branch, registry=registry) is not None:
|
|
437
441
|
sys.exit(1)
|
|
438
442
|
|
|
443
|
+
@main.command()
|
|
444
|
+
def track():
|
|
445
|
+
"""Start tracking a run of a shell script.
|
|
446
|
+
|
|
447
|
+
This command works like {func}`~lamindb.track()` in a Python session. Here is an example script:
|
|
448
|
+
|
|
449
|
+
```
|
|
450
|
+
# my_script.sh
|
|
451
|
+
set -e # exit on error
|
|
452
|
+
lamin track # initiate a tracked shell script run
|
|
453
|
+
lamin load --key raw/file1.txt
|
|
454
|
+
# do something
|
|
455
|
+
lamin save processed_file1.txt --key processed/file1.txt
|
|
456
|
+
lamin finish # mark the shell script run as finished
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
If you run that script, it will track the run of the script, and save the input and output artifacts:
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
sh my_script.sh
|
|
463
|
+
```
|
|
464
|
+
"""
|
|
465
|
+
from lamin_cli._context import track as track_
|
|
466
|
+
return track_()
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
@main.command()
|
|
470
|
+
def finish():
|
|
471
|
+
"""Finish the current tracked run of a shell script.
|
|
472
|
+
|
|
473
|
+
This command works like {func}`~lamindb.finish()` in a Python session.
|
|
474
|
+
"""
|
|
475
|
+
from lamin_cli._context import finish as finish_
|
|
476
|
+
return finish_()
|
|
477
|
+
|
|
439
478
|
|
|
440
479
|
@main.command()
|
|
441
480
|
@click.argument("entity", type=str, default=None, required=False)
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
from lamin_utils import logger
|
|
7
|
+
from lamindb_setup.core._settings_store import settings_dir
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_current_run_file() -> Path:
|
|
11
|
+
"""Get the path to the file storing the current run UID."""
|
|
12
|
+
return settings_dir / "current_shell_run.txt"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def is_interactive_shell() -> bool:
|
|
16
|
+
"""Check if running in an interactive terminal."""
|
|
17
|
+
return sys.stdin.isatty() and sys.stdout.isatty() and os.isatty(0)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_script_filename() -> Path:
|
|
21
|
+
"""Try to get the filename of the calling shell script."""
|
|
22
|
+
import psutil
|
|
23
|
+
|
|
24
|
+
parent = psutil.Process(os.getppid())
|
|
25
|
+
cmdline = parent.cmdline()
|
|
26
|
+
|
|
27
|
+
# For shells like bash, sh, zsh
|
|
28
|
+
if parent.name() in ["bash", "sh", "zsh", "dash"]:
|
|
29
|
+
# cmdline is typically: ['/bin/bash', 'script.sh', ...]
|
|
30
|
+
if len(cmdline) > 1 and not cmdline[1].startswith("-"):
|
|
31
|
+
return Path(cmdline[1])
|
|
32
|
+
raise click.ClickException(
|
|
33
|
+
"Cannot determine script filename. Please run in an interactive shell."
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def track():
|
|
38
|
+
import lamindb as ln
|
|
39
|
+
|
|
40
|
+
if not ln.setup.settings._instance_exists:
|
|
41
|
+
raise click.ClickException(
|
|
42
|
+
"Not connected to an instance. Please run: lamin connect account/name"
|
|
43
|
+
)
|
|
44
|
+
path = get_script_filename()
|
|
45
|
+
source_code = path.read_text()
|
|
46
|
+
transform = ln.Transform(
|
|
47
|
+
key=path.name, source_code=source_code, type="script"
|
|
48
|
+
).save()
|
|
49
|
+
run = ln.Run(transform=transform).save()
|
|
50
|
+
current_run_file = get_current_run_file()
|
|
51
|
+
current_run_file.parent.mkdir(parents=True, exist_ok=True)
|
|
52
|
+
current_run_file.write_text(run.uid)
|
|
53
|
+
logger.important(f"started tracking shell run: {run.uid}")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def finish():
|
|
57
|
+
from datetime import datetime, timezone
|
|
58
|
+
|
|
59
|
+
import lamindb as ln
|
|
60
|
+
|
|
61
|
+
if not ln.setup.settings._instance_exists:
|
|
62
|
+
raise click.ClickException(
|
|
63
|
+
"Not connected to an instance. Please run: lamin connect account/name"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
current_run_file = get_current_run_file()
|
|
67
|
+
if not current_run_file.exists():
|
|
68
|
+
raise click.ClickException(
|
|
69
|
+
"No active run to finish. Please run `lamin track` first."
|
|
70
|
+
)
|
|
71
|
+
run = ln.Run.get(uid=current_run_file.read_text().strip())
|
|
72
|
+
run._status_code = 0
|
|
73
|
+
run.finished_at = datetime.now(timezone.utc)
|
|
74
|
+
run.save()
|
|
75
|
+
current_run_file.unlink()
|
|
76
|
+
logger.important(f"finished tracking shell run: {run.uid}")
|
|
@@ -30,7 +30,7 @@ def io():
|
|
|
30
30
|
@click.option("--track/--no-track", is_flag=True, help="Whether to track snapshot generation.", default=True)
|
|
31
31
|
# fmt: on
|
|
32
32
|
def snapshot(upload: bool, track: bool) -> None:
|
|
33
|
-
"""Create
|
|
33
|
+
"""Create a SQLite snapshot of the connected instance."""
|
|
34
34
|
if not ln_setup.settings._instance_exists:
|
|
35
35
|
raise click.ClickException(
|
|
36
36
|
"Not connected to an instance. Please run: lamin connect account/name"
|
|
@@ -86,8 +86,8 @@ def snapshot(upload: bool, track: bool) -> None:
|
|
|
86
86
|
for table, (orig, clone) in mismatches.items()]
|
|
87
87
|
)
|
|
88
88
|
raise click.ClickException(error_msg)
|
|
89
|
-
except json.JSONDecodeError:
|
|
90
|
-
|
|
89
|
+
except (json.JSONDecodeError, AttributeError, ValueError, TypeError):
|
|
90
|
+
raise click.ClickException(f"Clone verification failed:\n{result.stderr}") from None
|
|
91
91
|
|
|
92
92
|
|
|
93
93
|
ln_setup.connect(f"{instance_owner}/{instance_name}", use_root_db_user=True)
|
|
@@ -6,6 +6,7 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
from lamin_utils import logger
|
|
8
8
|
|
|
9
|
+
from ._context import get_current_run_file
|
|
9
10
|
from ._save import infer_registry_from_path, parse_title_r_notebook
|
|
10
11
|
from .urls import decompose_url
|
|
11
12
|
|
|
@@ -48,6 +49,10 @@ def load(
|
|
|
48
49
|
ln_setup.connect(instance)
|
|
49
50
|
import lamindb as ln
|
|
50
51
|
|
|
52
|
+
current_run = None
|
|
53
|
+
if get_current_run_file().exists():
|
|
54
|
+
current_run = ln.Run.get(uid=get_current_run_file().read_text().strip())
|
|
55
|
+
|
|
51
56
|
def script_to_notebook(
|
|
52
57
|
transform: ln.Transform, notebook_path: Path, bump_revision: bool = False
|
|
53
58
|
) -> None:
|
|
@@ -182,7 +187,7 @@ def load(
|
|
|
182
187
|
entities = entities.order_by("-created_at")
|
|
183
188
|
|
|
184
189
|
entity_obj = entities.first()
|
|
185
|
-
cache_path = entity_obj.cache()
|
|
190
|
+
cache_path = entity_obj.cache(is_run_input=current_run)
|
|
186
191
|
|
|
187
192
|
# collection gives us a list of paths
|
|
188
193
|
if isinstance(cache_path, list):
|
|
@@ -9,17 +9,23 @@ import lamindb_setup as ln_setup
|
|
|
9
9
|
from lamin_utils import logger
|
|
10
10
|
from lamindb_setup.core.hashing import hash_file
|
|
11
11
|
|
|
12
|
+
from lamin_cli._context import get_current_run_file
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
def infer_registry_from_path(path: Path | str) -> str:
|
|
14
16
|
suffixes_transform = {
|
|
15
17
|
"py": {".py", ".ipynb"},
|
|
16
18
|
"R": {".R", ".qmd", ".Rmd"},
|
|
19
|
+
"sh": {".sh"},
|
|
17
20
|
}
|
|
18
21
|
if isinstance(path, str):
|
|
19
22
|
path = Path(path)
|
|
20
23
|
registry = (
|
|
21
24
|
"transform"
|
|
22
|
-
if path.suffix
|
|
25
|
+
if path.suffix
|
|
26
|
+
in suffixes_transform["py"]
|
|
27
|
+
.union(suffixes_transform["R"])
|
|
28
|
+
.union(suffixes_transform["sh"])
|
|
23
29
|
else "artifact"
|
|
24
30
|
)
|
|
25
31
|
return registry
|
|
@@ -42,9 +48,11 @@ def parse_uid_from_code(content: str, suffix: str) -> str | None:
|
|
|
42
48
|
r'track\(\s*(?:transform\s*=\s*)?([\'"])([a-zA-Z0-9]{12,16})\1'
|
|
43
49
|
)
|
|
44
50
|
uid_pattern = None
|
|
51
|
+
elif suffix == ".sh":
|
|
52
|
+
return None
|
|
45
53
|
else:
|
|
46
54
|
raise SystemExit(
|
|
47
|
-
"Only .py, .ipynb, .R, .qmd, .Rmd files are supported for saving"
|
|
55
|
+
"Only .py, .ipynb, .R, .qmd, .Rmd, .sh files are supported for saving"
|
|
48
56
|
" transforms."
|
|
49
57
|
)
|
|
50
58
|
|
|
@@ -82,8 +90,13 @@ def save(
|
|
|
82
90
|
) -> str | None:
|
|
83
91
|
import lamindb as ln
|
|
84
92
|
from lamindb._finish import save_context_core
|
|
93
|
+
from lamindb_setup.core._settings_store import settings_dir
|
|
85
94
|
from lamindb_setup.core.upath import LocalPathClasses, UPath, create_path
|
|
86
95
|
|
|
96
|
+
current_run = None
|
|
97
|
+
if get_current_run_file().exists():
|
|
98
|
+
current_run = ln.Run.get(uid=get_current_run_file().read_text().strip())
|
|
99
|
+
|
|
87
100
|
# this allows to have the correct treatment of credentials in case of cloud paths
|
|
88
101
|
ppath = create_path(path)
|
|
89
102
|
# isinstance is needed to cast the type of path to UPath
|
|
@@ -149,6 +162,7 @@ def save(
|
|
|
149
162
|
revises=revises,
|
|
150
163
|
branch=branch_record,
|
|
151
164
|
space=space_record,
|
|
165
|
+
run=current_run,
|
|
152
166
|
).save()
|
|
153
167
|
logger.important(f"saved: {artifact}")
|
|
154
168
|
logger.important(f"storage path: {artifact.path}")
|
|
@@ -219,7 +233,7 @@ def save(
|
|
|
219
233
|
if transform is None:
|
|
220
234
|
uid = f"{stem_uid}0000"
|
|
221
235
|
else:
|
|
222
|
-
transform_hash, _ = hash_file(ppath)
|
|
236
|
+
_, transform_hash, _ = hash_file(ppath)
|
|
223
237
|
transform = ln.Transform.filter(hash=transform_hash).first()
|
|
224
238
|
if transform is not None and transform.hash is not None:
|
|
225
239
|
if transform.hash == transform_hash:
|
|
@@ -268,7 +282,7 @@ def save(
|
|
|
268
282
|
uid=uid,
|
|
269
283
|
description=description,
|
|
270
284
|
key=key,
|
|
271
|
-
type="script" if ppath.suffix in {".R", ".py"} else "notebook",
|
|
285
|
+
type="script" if ppath.suffix in {".R", ".py", ".sh"} else "notebook",
|
|
272
286
|
revises=revises,
|
|
273
287
|
)
|
|
274
288
|
if space is not None:
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import lamindb as ln
|
|
6
|
+
|
|
7
|
+
scripts_dir = Path(__file__).parent.parent.resolve() / "scripts"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_save_shell_script():
|
|
11
|
+
"""Test that saving a shell script creates a transform with source_code and type='script'."""
|
|
12
|
+
env = os.environ.copy()
|
|
13
|
+
env["LAMIN_TESTING"] = "true"
|
|
14
|
+
|
|
15
|
+
script_path = scripts_dir / "test-shell-script.sh"
|
|
16
|
+
script_content = "#!/bin/bash\necho 'test script'\n"
|
|
17
|
+
script_path.write_text(script_content)
|
|
18
|
+
|
|
19
|
+
result = subprocess.run(
|
|
20
|
+
f"lamin save {script_path}",
|
|
21
|
+
shell=True,
|
|
22
|
+
env=env,
|
|
23
|
+
capture_output=True,
|
|
24
|
+
)
|
|
25
|
+
assert result.returncode == 0
|
|
26
|
+
assert "created Transform" in result.stdout.decode()
|
|
27
|
+
|
|
28
|
+
transform = ln.Transform.get(key=script_path.name)
|
|
29
|
+
|
|
30
|
+
assert transform.source_code is not None, (
|
|
31
|
+
"Transform source_code should be populated"
|
|
32
|
+
)
|
|
33
|
+
assert len(transform.source_code) > 0, "Transform source_code should not be empty"
|
|
34
|
+
assert transform.type == "script", (
|
|
35
|
+
f"Transform type should be 'script', got '{transform.type}'"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
script_path.unlink()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import lamindb as ln
|
|
6
|
+
|
|
7
|
+
scripts_dir = Path(__file__).parent.parent.resolve() / "scripts"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_track_lineage_via_cli():
|
|
11
|
+
"""Test that lineage tracking works correctly via CLI commands."""
|
|
12
|
+
env = os.environ.copy()
|
|
13
|
+
env["LAMIN_TESTING"] = "true"
|
|
14
|
+
|
|
15
|
+
script_path = scripts_dir / "track-lineage.sh"
|
|
16
|
+
|
|
17
|
+
# Make the script executable
|
|
18
|
+
script_path.chmod(0o755)
|
|
19
|
+
|
|
20
|
+
# Run the shell script
|
|
21
|
+
result = subprocess.run(
|
|
22
|
+
["sh", str(script_path)],
|
|
23
|
+
env=env,
|
|
24
|
+
capture_output=True,
|
|
25
|
+
text=True,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Check that the script ran successfully
|
|
29
|
+
if result.returncode != 0:
|
|
30
|
+
print(f"Script stdout: {result.stdout}")
|
|
31
|
+
print(f"Script stderr: {result.stderr}")
|
|
32
|
+
assert result.returncode == 0, (
|
|
33
|
+
f"Script failed:\nstdout: {result.stdout}\nstderr: {result.stderr}"
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
transform = ln.Transform.get(key="track-lineage.sh")
|
|
37
|
+
assert transform.type == "script"
|
|
38
|
+
run = transform.latest_run
|
|
39
|
+
|
|
40
|
+
input_artifacts = run.input_artifacts.all()
|
|
41
|
+
assert len(input_artifacts) == 1, (
|
|
42
|
+
f"Expected 1 input artifact, got {len(input_artifacts)}"
|
|
43
|
+
)
|
|
44
|
+
input_artifact = input_artifacts[0]
|
|
45
|
+
assert input_artifact.key == "test/input.txt", (
|
|
46
|
+
f"Expected input artifact key 'test/input.txt', got '{input_artifact.key}'"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
output_artifacts = run.output_artifacts.all()
|
|
50
|
+
assert len(output_artifacts) == 1, (
|
|
51
|
+
f"Expected 1 output artifact, got {len(output_artifacts)}"
|
|
52
|
+
)
|
|
53
|
+
output_artifact = output_artifacts[0]
|
|
54
|
+
assert output_artifact.key == "test/output.txt", (
|
|
55
|
+
f"Expected output artifact key 'test/output.txt', got '{output_artifact.key}'"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
assert run.finished_at is not None, "Run should be finished"
|
|
59
|
+
assert run._status_code == 0, "Run status code should be 0 (finished)"
|
|
60
|
+
|
|
61
|
+
# Clean up
|
|
62
|
+
input_artifact.delete(permanent=True)
|
|
63
|
+
output_artifact.delete(permanent=True)
|
|
64
|
+
run.delete(permanent=True)
|
|
65
|
+
transform.delete(permanent=True)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# prepare test data
|
|
2
|
+
TEST_DIR=$(mktemp -d)
|
|
3
|
+
trap "rm -rf $TEST_DIR" EXIT
|
|
4
|
+
INPUT_FILE="$TEST_DIR/test_input.txt"
|
|
5
|
+
OUTPUT_FILE="$TEST_DIR/test_output.txt"
|
|
6
|
+
echo "test input data" > "$INPUT_FILE"
|
|
7
|
+
lamin save "$INPUT_FILE" --key test/input.txt
|
|
8
|
+
|
|
9
|
+
# actual script
|
|
10
|
+
set -e # exit on error
|
|
11
|
+
lamin track
|
|
12
|
+
lamin load --key test/input.txt
|
|
13
|
+
echo "test output data" > "$OUTPUT_FILE"
|
|
14
|
+
lamin save "$OUTPUT_FILE" --key test/output.txt
|
|
15
|
+
lamin finish
|
|
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
|
{lamin_cli-1.10.0 → lamin_cli-1.11.0}/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
|