labnotes 0.0.27__tar.gz → 0.0.29__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.27 → labnotes-0.0.29}/PKG-INFO +1 -1
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/context.md +3 -1
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/models.py +1 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/describe_event.py +4 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/run_experiment.py +24 -6
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tui/run_app.py +17 -1
- {labnotes-0.0.27 → labnotes-0.0.29}/.gitignore +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/CHANGELOG.md +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/MIGRATIONS.md +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/README.md +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/pyproject.toml +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/cell_progress.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/create_app.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/experiment_progress.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/experiments.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/init.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/load_experiment.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/pivot_cells.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/execute_run.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/lock_path.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/read_experiment_config.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/start_experiment.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/wait_for_group.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/server.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/static/labnotes.css +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/stop_idle_kernels.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/summarize_config.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/open_tables.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/templates/experiment.html +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/templates/macros.html +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.27 → labnotes-0.0.29}/uv.lock +0 -0
|
@@ -90,7 +90,9 @@ open when they finish. Runs not started yet show grayed as `pending`, and as
|
|
|
90
90
|
`skipped` once stopped. Move the cursor to a pending run and press `n` to run it
|
|
91
91
|
next. `d` deselects a pending run so it won't run; `d` again puts it back at
|
|
92
92
|
the end of the queue. `/` filters rows by params text; Enter keeps the filter,
|
|
93
|
-
Escape clears it.
|
|
93
|
+
Escape clears it. `D` deselects every pending run the filter shows. `k` stops the
|
|
94
|
+
running run under the cursor; `k` again kills it. `+` and
|
|
95
|
+
`-` change how many run at once; lowering it lets running runs
|
|
94
96
|
finish; `q` quits. Elsewhere, or with `--no-tui`, it prints a
|
|
95
97
|
line per event instead.
|
|
96
98
|
|
|
@@ -18,6 +18,10 @@ def describe_event(event: dict[str, object]) -> str:
|
|
|
18
18
|
return f"{event['run']} deselected."
|
|
19
19
|
case "reselected":
|
|
20
20
|
return f"{event['run']} back in the queue."
|
|
21
|
+
case "stopping run":
|
|
22
|
+
return f"Stopping {event['run']}."
|
|
23
|
+
case "killing run":
|
|
24
|
+
return f"Killing {event['run']}."
|
|
21
25
|
case "concurrency":
|
|
22
26
|
return f"{event['concurrency']} at a time."
|
|
23
27
|
case "stopping":
|
|
@@ -48,25 +48,41 @@ async def run_experiment(
|
|
|
48
48
|
plan.append((run, params))
|
|
49
49
|
runs = [{"run": run, "params": params} for run, params in plan]
|
|
50
50
|
report({"event": "planned", "runs": runs, "concurrency": concurrency})
|
|
51
|
-
running: set[asyncio.subprocess.Process] =
|
|
51
|
+
running: dict[str, set[asyncio.subprocess.Process]] = {}
|
|
52
|
+
stopped: set[str] = set()
|
|
52
53
|
results: list[dict[str, object]] = []
|
|
53
54
|
stopping = asyncio.Event()
|
|
54
55
|
|
|
55
|
-
def
|
|
56
|
-
for process in running:
|
|
56
|
+
def signal_run(run: str, signal_number: int) -> None:
|
|
57
|
+
for process in running[run]:
|
|
57
58
|
with contextlib.suppress(ProcessLookupError):
|
|
58
59
|
os.killpg(process.pid, signal_number)
|
|
59
60
|
|
|
61
|
+
def signal_runs(signal_number: int) -> None:
|
|
62
|
+
for run in running:
|
|
63
|
+
signal_run(run, signal_number)
|
|
64
|
+
|
|
60
65
|
def interrupt() -> None:
|
|
61
66
|
plan.clear()
|
|
62
67
|
if not stopping.is_set():
|
|
63
68
|
stopping.set()
|
|
64
69
|
report({"event": "stopping"})
|
|
65
70
|
signal_runs(signal.SIGTERM)
|
|
66
|
-
elif running:
|
|
71
|
+
elif any(running.values()):
|
|
67
72
|
report({"event": "killing"})
|
|
68
73
|
signal_runs(signal.SIGKILL)
|
|
69
74
|
|
|
75
|
+
def kill(run: str) -> None:
|
|
76
|
+
if not running.get(run):
|
|
77
|
+
return
|
|
78
|
+
if run in stopped:
|
|
79
|
+
report({"event": "killing run", "run": run})
|
|
80
|
+
signal_run(run, signal.SIGKILL)
|
|
81
|
+
else:
|
|
82
|
+
stopped.add(run)
|
|
83
|
+
report({"event": "stopping run", "run": run})
|
|
84
|
+
signal_run(run, signal.SIGTERM)
|
|
85
|
+
|
|
70
86
|
held: list[Entry] = []
|
|
71
87
|
|
|
72
88
|
def find(entries: list[Entry], run: str) -> Entry | None:
|
|
@@ -125,13 +141,15 @@ async def run_experiment(
|
|
|
125
141
|
folder = run_folder(config.root, config.runs, name, run)
|
|
126
142
|
folder.mkdir(parents=True)
|
|
127
143
|
report({"event": "started", "run": run, "params": params})
|
|
144
|
+
running[run] = set()
|
|
128
145
|
result = await execute_run(
|
|
129
|
-
config.root, folder, experiment.command, params, running, stopping
|
|
146
|
+
config.root, folder, experiment.command, params, running[run], stopping
|
|
130
147
|
)
|
|
148
|
+
del running[run]
|
|
131
149
|
report({"event": "ended", "run": run, **result})
|
|
132
150
|
results.append(result)
|
|
133
151
|
|
|
134
|
-
listen(Controls(interrupt, run_next, set_concurrency, toggle))
|
|
152
|
+
listen(Controls(interrupt, run_next, set_concurrency, toggle, kill))
|
|
135
153
|
fill()
|
|
136
154
|
await idle.wait()
|
|
137
155
|
await asyncio.gather(*started)
|
|
@@ -20,6 +20,8 @@ class RunApp(App[None]):
|
|
|
20
20
|
Binding("ctrl+c", "interrupt", "Stop runs"),
|
|
21
21
|
Binding("n", "run_next", "Run next"),
|
|
22
22
|
Binding("d", "toggle_run", "Deselect"),
|
|
23
|
+
Binding("D", "deselect_shown", "Deselect shown"),
|
|
24
|
+
Binding("k", "kill_run", "Stop run"),
|
|
23
25
|
Binding("slash", "filter", "Filter", key_display="/"),
|
|
24
26
|
Binding("escape", "clear_filter", "Clear filter"),
|
|
25
27
|
Binding("plus,equals_sign", "concurrency(1)", "More at once", key_display="+"),
|
|
@@ -33,7 +35,11 @@ class RunApp(App[None]):
|
|
|
33
35
|
self.title = title
|
|
34
36
|
self.results: list[dict[str, object]] = []
|
|
35
37
|
self.controls = Controls(
|
|
36
|
-
lambda: None,
|
|
38
|
+
lambda: None,
|
|
39
|
+
lambda run: None,
|
|
40
|
+
lambda count: None,
|
|
41
|
+
lambda run: None,
|
|
42
|
+
lambda run: None,
|
|
37
43
|
)
|
|
38
44
|
self.concurrency = 1
|
|
39
45
|
self.done = False
|
|
@@ -177,6 +183,16 @@ class RunApp(App[None]):
|
|
|
177
183
|
if (run := self.cursor_run()) is not None:
|
|
178
184
|
self.controls.toggle(run)
|
|
179
185
|
|
|
186
|
+
def action_kill_run(self) -> None:
|
|
187
|
+
if (run := self.cursor_run()) is not None:
|
|
188
|
+
self.controls.kill(run)
|
|
189
|
+
|
|
190
|
+
def action_deselect_shown(self) -> None:
|
|
191
|
+
shown = [str(key.value) for key in self.query_one(DataTable).rows]
|
|
192
|
+
for run in shown:
|
|
193
|
+
if run in self.pending:
|
|
194
|
+
self.controls.toggle(run)
|
|
195
|
+
|
|
180
196
|
def action_concurrency(self, change: int) -> None:
|
|
181
197
|
self.controls.set_concurrency(self.concurrency + change)
|
|
182
198
|
|
|
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
|
|
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
|