labnotes 0.0.20__tar.gz → 0.0.22__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.
- {labnotes-0.0.20 → labnotes-0.0.22}/PKG-INFO +1 -1
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/context.md +16 -8
- labnotes-0.0.22/src/labnotes/default_counts.py +2 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/experiments.py +4 -1
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/models.py +3 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/execute_run.py +7 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/read_experiment_config.py +6 -2
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/run_experiment.py +6 -11
- labnotes-0.0.22/src/labnotes/runner/wait_for_group.py +11 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/summarize_config.py +3 -3
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/experiment_rows.py +9 -1
- labnotes-0.0.22/src/labnotes/tables/list_experiment_runs.py +16 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/open_tables.py +2 -2
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/read_run.py +1 -2
- labnotes-0.0.20/src/labnotes/tables/list_experiment_runs.py +0 -11
- {labnotes-0.0.20 → labnotes-0.0.22}/.gitignore +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/CHANGELOG.md +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/MIGRATIONS.md +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/README.md +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/pyproject.toml +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/cell_progress.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/create_app.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/experiment_progress.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/init.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/load_experiment.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/pivot_cells.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/lock_path.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/start_experiment.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/server.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/static/labnotes.css +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/stop_idle_kernels.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/templates/experiment.html +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/templates/macros.html +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.20 → labnotes-0.0.22}/uv.lock +0 -0
|
@@ -22,8 +22,8 @@ Paths are relative to the root. `{experiment}` and `{run}` stand for folder name
|
|
|
22
22
|
`experiment.toml`, files or runs.
|
|
23
23
|
- `files`: `experiment`, `filename`, `path`, `ctime`, `mtime`.
|
|
24
24
|
- `runs`: `experiment`, `id`, `folder`, `created`, `ended`, `exit_code`,
|
|
25
|
-
`error`, `stdout`, `stderr`,
|
|
26
|
-
folder's `run.json`.
|
|
25
|
+
`error`, `stdout`, `stderr`, `params` as JSON, and `run`, the whole
|
|
26
|
+
`run.json` as JSON, read from each run folder's `run.json`.
|
|
27
27
|
|
|
28
28
|
Keys come from folder names, so renaming a folder renames its rows on the next
|
|
29
29
|
read. Paths are relative to the root, and times are UTC ISO 8601.
|
|
@@ -57,7 +57,8 @@ effort = ["low", "high"]
|
|
|
57
57
|
and as `display_name` in `experiments`. The folder name stays the key, so
|
|
58
58
|
renaming the experiment means editing `name`.
|
|
59
59
|
Each combination of `[params]` runs `repeats` times, round-robin. Only runs
|
|
60
|
-
that
|
|
60
|
+
that count fill a combination, so running again fills in what failed or never
|
|
61
|
+
ran.
|
|
61
62
|
Each run gets a new run folder. `command` runs in a shell from the root, with
|
|
62
63
|
`{run_dir}` replaced by the run folder's absolute path. Pass it to the
|
|
63
64
|
script so it writes its outputs there.
|
|
@@ -65,15 +66,22 @@ Each param is appended as `--name value`, with the name as written (use
|
|
|
65
66
|
`source-language` for a `--source-language` flag), booleans as 0 or 1, and lists
|
|
66
67
|
and tables as JSON. labnotes writes `run.json` (params, created, ended, exit
|
|
67
68
|
code), `stdout.log` and `stderr.log` to the run folder.
|
|
68
|
-
|
|
69
|
-
`
|
|
70
|
-
replaces it
|
|
69
|
+
A run counts when it exited 0 and its `run.json` doesn't hold
|
|
70
|
+
`"excluded": true`, so excluding a run by hand means adding that key; the next
|
|
71
|
+
`run` replaces it. Other keys you add to `run.json` are kept and show in `run`.
|
|
72
|
+
`counts` in `experiment.toml` replaces the rule with a SQL condition over
|
|
73
|
+
`runs`, for example `counts = "exit_code = 0 AND created > '2026-10-01'"` to
|
|
74
|
+
drop runs from before a script change. The default is
|
|
75
|
+
`exit_code = 0 AND NOT coalesce(run ->> 'excluded', false)`.
|
|
76
|
+
`experiments foo runs` shows the rule's result as `counted`; queries over
|
|
77
|
+
`runs` itself see every run, so apply the rule there too.
|
|
71
78
|
|
|
72
79
|
A script run by labnotes should:
|
|
73
80
|
|
|
74
81
|
- take each param as a `--name value` flag, parsing lists and tables from JSON;
|
|
75
82
|
- take its output folder as an argument, passed in `command` as `{run_dir}`, and write everything it produces there;
|
|
76
|
-
- exit non-zero when it fails, so the run counts as failed and runs again next time
|
|
83
|
+
- exit non-zero when it fails, so the run counts as failed and runs again next time;
|
|
84
|
+
- stop any background process it starts before it exits; the run ends when `command` exits.
|
|
77
85
|
|
|
78
86
|
Runs can take a long time. Start `run` in the background, then check on it with `labnotes experiments foo runs`; a run with no `ended` is still going.
|
|
79
87
|
|
|
@@ -89,4 +97,4 @@ line per event instead.
|
|
|
89
97
|
- A second `run` of the same experiment fails while the first holds the lock.
|
|
90
98
|
- It exits 1 if any run failed.
|
|
91
99
|
|
|
92
|
-
`uvx labnotes server` serves the experiments, their markdown files and their runs as a local site, read from the same `.labnotes.toml`. A grid cell is full once it has `repeats` runs that
|
|
100
|
+
`uvx labnotes server` serves the experiments, their markdown files and their runs as a local site, read from the same `.labnotes.toml`. A grid cell is full once it has `repeats` runs that count.
|
|
@@ -82,7 +82,10 @@ def experiments(
|
|
|
82
82
|
ctx.exit(1 if failed else 0)
|
|
83
83
|
if action not in (None, "runs"):
|
|
84
84
|
raise click.UsageError(f"Unknown action {action}. Use runs or run.")
|
|
85
|
-
|
|
85
|
+
try:
|
|
86
|
+
rows = asyncio.run(experiment_rows(config, name, action))
|
|
87
|
+
except ValueError as error:
|
|
88
|
+
raise click.ClickException(str(error)) from error
|
|
86
89
|
if rows is None:
|
|
87
90
|
raise click.ClickException(f"No experiment named {name}.")
|
|
88
91
|
print_rows(rows, output_format)
|
|
@@ -3,6 +3,8 @@ from dataclasses import dataclass, field
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
+
from labnotes.default_counts import DEFAULT_COUNTS
|
|
7
|
+
|
|
6
8
|
Report = Callable[[dict[str, object]], None]
|
|
7
9
|
Listen = Callable[[Callable[[], None]], None]
|
|
8
10
|
Start = Callable[[Report, Listen], Coroutine[Any, Any, list[dict[str, object]]]]
|
|
@@ -31,3 +33,4 @@ class ExperimentConfig:
|
|
|
31
33
|
repeats: int
|
|
32
34
|
params: dict[str, list[object]]
|
|
33
35
|
name: str | None = None
|
|
36
|
+
counts: str = DEFAULT_COUNTS
|
|
@@ -3,6 +3,7 @@ import time
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
from labnotes.runner.shell_command import shell_command
|
|
6
|
+
from labnotes.runner.wait_for_group import wait_for_group
|
|
6
7
|
from labnotes.runner.write_run_json import write_run_json
|
|
7
8
|
from labnotes.tables.iso_time import iso_time
|
|
8
9
|
|
|
@@ -13,6 +14,7 @@ async def execute_run(
|
|
|
13
14
|
command: str,
|
|
14
15
|
params: dict[str, object],
|
|
15
16
|
running: set[asyncio.subprocess.Process],
|
|
17
|
+
stopping: asyncio.Event,
|
|
16
18
|
) -> dict[str, object]:
|
|
17
19
|
run: dict[str, object] = {
|
|
18
20
|
"params": params,
|
|
@@ -42,6 +44,11 @@ async def execute_run(
|
|
|
42
44
|
running.add(process)
|
|
43
45
|
try:
|
|
44
46
|
run["exit_code"] = await process.wait()
|
|
47
|
+
# The shell can exit on SIGTERM before what it started. Once
|
|
48
|
+
# stopping, the run stays killable until its whole group is
|
|
49
|
+
# gone. A normal exit ends the run even if helpers linger.
|
|
50
|
+
if stopping.is_set():
|
|
51
|
+
await wait_for_group(process.pid)
|
|
45
52
|
finally:
|
|
46
53
|
running.discard(process)
|
|
47
54
|
run["ended"] = iso_time(time.time())
|
|
@@ -2,9 +2,10 @@ import json
|
|
|
2
2
|
import tomllib
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
|
+
from labnotes.default_counts import DEFAULT_COUNTS
|
|
5
6
|
from labnotes.models import ExperimentConfig
|
|
6
7
|
|
|
7
|
-
SETTINGS = {"name", "command", "repeats", "params"}
|
|
8
|
+
SETTINGS = {"name", "command", "repeats", "params", "counts"}
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def read_experiment_config(path: Path) -> ExperimentConfig:
|
|
@@ -35,4 +36,7 @@ def read_experiment_config(path: Path) -> ExperimentConfig:
|
|
|
35
36
|
json.dumps(params)
|
|
36
37
|
except TypeError as error:
|
|
37
38
|
raise ValueError(f"{path}: params can't hold dates or times") from error
|
|
38
|
-
|
|
39
|
+
counts = values.get("counts", DEFAULT_COUNTS)
|
|
40
|
+
if not isinstance(counts, str) or not counts.strip():
|
|
41
|
+
raise ValueError(f"{path}: counts must be a SQL condition")
|
|
42
|
+
return ExperimentConfig(command, repeats, params, name, counts)
|
|
@@ -22,17 +22,13 @@ async def run_experiment(
|
|
|
22
22
|
report: Report,
|
|
23
23
|
listen: Listen,
|
|
24
24
|
) -> list[dict[str, object]]:
|
|
25
|
-
runs = await list_experiment_runs(open_tables(config), name)
|
|
26
|
-
finished = [
|
|
27
|
-
json.loads(str(run["params"]))
|
|
28
|
-
for run in runs
|
|
29
|
-
if run["exit_code"] == 0 and not run["excluded"]
|
|
30
|
-
]
|
|
25
|
+
runs = await list_experiment_runs(open_tables(config), name, experiment.counts)
|
|
26
|
+
finished = [json.loads(str(run["params"])) for run in runs if run["counted"]]
|
|
31
27
|
plan = plan_runs(param_grid(experiment.params), experiment.repeats, finished)
|
|
32
28
|
report({"event": "planned", "runs": len(plan), "concurrency": concurrency})
|
|
33
29
|
running: set[asyncio.subprocess.Process] = set()
|
|
34
30
|
results: list[dict[str, object]] = []
|
|
35
|
-
stopping =
|
|
31
|
+
stopping = asyncio.Event()
|
|
36
32
|
|
|
37
33
|
def signal_runs(signal_number: int) -> None:
|
|
38
34
|
for process in running:
|
|
@@ -40,10 +36,9 @@ async def run_experiment(
|
|
|
40
36
|
os.killpg(process.pid, signal_number)
|
|
41
37
|
|
|
42
38
|
def interrupt() -> None:
|
|
43
|
-
nonlocal stopping
|
|
44
39
|
plan.clear()
|
|
45
|
-
if not stopping:
|
|
46
|
-
stopping
|
|
40
|
+
if not stopping.is_set():
|
|
41
|
+
stopping.set()
|
|
47
42
|
report({"event": "stopping"})
|
|
48
43
|
signal_runs(signal.SIGTERM)
|
|
49
44
|
elif running:
|
|
@@ -60,7 +55,7 @@ async def run_experiment(
|
|
|
60
55
|
folder.mkdir(parents=True)
|
|
61
56
|
report({"event": "started", "run": run, "params": params})
|
|
62
57
|
result = await execute_run(
|
|
63
|
-
config.root, folder, experiment.command, params, running
|
|
58
|
+
config.root, folder, experiment.command, params, running, stopping
|
|
64
59
|
)
|
|
65
60
|
report({"event": "ended", "run": run, **result})
|
|
66
61
|
results.append(result)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from math import prod
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
COMPLETED_RUN = "exit_code = 0 AND NOT coalesce(excluded, 0)"
|
|
3
|
+
from labnotes.default_counts import DEFAULT_COUNTS
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
def summarize_config(config: dict[str, object] | None) -> dict[str, object]:
|
|
@@ -15,12 +14,13 @@ def summarize_config(config: dict[str, object] | None) -> dict[str, object]:
|
|
|
15
14
|
repeats = repeats if isinstance(repeats, int) else None
|
|
16
15
|
cells = prod(len(values) for values in varies.values())
|
|
17
16
|
command = config.get("command")
|
|
17
|
+
counts = config.get("counts")
|
|
18
18
|
return {
|
|
19
19
|
"fixed": {},
|
|
20
20
|
"varies": varies,
|
|
21
21
|
"cell_count": cells,
|
|
22
22
|
"runs_per_cell": repeats,
|
|
23
23
|
"target": cells * repeats if repeats is not None else None,
|
|
24
|
-
"completed_run":
|
|
24
|
+
"completed_run": counts if isinstance(counts, str) else DEFAULT_COUNTS,
|
|
25
25
|
"other": {} if command is None else {"command": command},
|
|
26
26
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
+
import json
|
|
2
|
+
|
|
1
3
|
from labnotes.models import LabnotesConfig
|
|
4
|
+
from labnotes.summarize_config import summarize_config
|
|
2
5
|
from labnotes.tables.list_experiment_runs import list_experiment_runs
|
|
3
6
|
from labnotes.tables.open_tables import open_tables
|
|
4
7
|
from labnotes.tables.summarize_experiments import summarize_experiments
|
|
@@ -13,5 +16,10 @@ async def experiment_rows(
|
|
|
13
16
|
return None
|
|
14
17
|
match name, action:
|
|
15
18
|
case str(), "runs":
|
|
16
|
-
|
|
19
|
+
[row] = rows
|
|
20
|
+
stored = row["config"]
|
|
21
|
+
summary = summarize_config(json.loads(str(stored)) if stored else None)
|
|
22
|
+
return await list_experiment_runs(
|
|
23
|
+
tables, name, str(summary["completed_run"])
|
|
24
|
+
)
|
|
17
25
|
return rows
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from dirsql import DirSQL
|
|
2
|
+
|
|
3
|
+
from labnotes.sql_quote import quote_string
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
async def list_experiment_runs(
|
|
7
|
+
tables: DirSQL, name: str, counts: str
|
|
8
|
+
) -> list[dict[str, object]]:
|
|
9
|
+
try:
|
|
10
|
+
return await tables.query(
|
|
11
|
+
f"SELECT id, created, ended, exit_code, error, ({counts}) AS counted,"
|
|
12
|
+
" params, folder FROM runs"
|
|
13
|
+
f" WHERE experiment = {quote_string(name)} ORDER BY created, id"
|
|
14
|
+
)
|
|
15
|
+
except RuntimeError as error:
|
|
16
|
+
raise ValueError(f"{name}: counts doesn't work: {error}") from error
|
|
@@ -31,8 +31,8 @@ CREATE TABLE files (
|
|
|
31
31
|
RUNS = """
|
|
32
32
|
CREATE TABLE runs (
|
|
33
33
|
experiment TEXT, id TEXT, folder TEXT, created TEXT, ended TEXT,
|
|
34
|
-
exit_code INTEGER, error TEXT,
|
|
35
|
-
stdout TEXT, stderr TEXT, params TEXT,
|
|
34
|
+
exit_code INTEGER, error TEXT,
|
|
35
|
+
stdout TEXT, stderr TEXT, params TEXT, run TEXT,
|
|
36
36
|
PRIMARY KEY (experiment, id)
|
|
37
37
|
)
|
|
38
38
|
"""
|
|
@@ -20,10 +20,9 @@ def read_run(root: Path, template: str, path: str) -> list[dict[str, object]]:
|
|
|
20
20
|
"ended": run.get("ended"),
|
|
21
21
|
"exit_code": run.get("exit_code"),
|
|
22
22
|
"error": run.get("error"),
|
|
23
|
-
"excluded": run.get("excluded") is True,
|
|
24
|
-
"reason": run.get("reason"),
|
|
25
23
|
"stdout": f"{folder}/stdout.log",
|
|
26
24
|
"stderr": f"{folder}/stderr.log",
|
|
27
25
|
"params": json.dumps(run.get("params", {})),
|
|
26
|
+
"run": json.dumps(run),
|
|
28
27
|
}
|
|
29
28
|
]
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
from dirsql import DirSQL
|
|
2
|
-
|
|
3
|
-
from labnotes.sql_quote import quote_string
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
async def list_experiment_runs(tables: DirSQL, name: str) -> list[dict[str, object]]:
|
|
7
|
-
return await tables.query(
|
|
8
|
-
"SELECT id, created, ended, exit_code, error, excluded, reason, params, folder"
|
|
9
|
-
" FROM runs"
|
|
10
|
-
f" WHERE experiment = {quote_string(name)} ORDER BY created, id"
|
|
11
|
-
)
|
|
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
|
|
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
|