labnotes 0.0.20__tar.gz → 0.0.21__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.
Files changed (87) hide show
  1. {labnotes-0.0.20 → labnotes-0.0.21}/PKG-INFO +1 -1
  2. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/context.md +2 -1
  3. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/execute_run.py +7 -0
  4. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/run_experiment.py +4 -5
  5. labnotes-0.0.21/src/labnotes/runner/wait_for_group.py +11 -0
  6. {labnotes-0.0.20 → labnotes-0.0.21}/.gitignore +0 -0
  7. {labnotes-0.0.20 → labnotes-0.0.21}/CHANGELOG.md +0 -0
  8. {labnotes-0.0.20 → labnotes-0.0.21}/MIGRATIONS.md +0 -0
  9. {labnotes-0.0.20 → labnotes-0.0.21}/README.md +0 -0
  10. {labnotes-0.0.20 → labnotes-0.0.21}/pyproject.toml +0 -0
  11. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/__init__.py +0 -0
  12. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/asset_url.py +0 -0
  13. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/cell_progress.py +0 -0
  14. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/cli.py +0 -0
  15. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/context_guide.py +0 -0
  16. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/count_runs.py +0 -0
  17. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/create_app.py +0 -0
  18. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/create_experiment.py +0 -0
  19. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/data_html.py +0 -0
  20. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/default_config_text.py +0 -0
  21. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/drain_watch.py +0 -0
  22. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/experiment_notes.py +0 -0
  23. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/experiment_progress.py +0 -0
  24. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/experiment_runs.py +0 -0
  25. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/experiments.py +0 -0
  26. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/format_value.py +0 -0
  27. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/init.py +0 -0
  28. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/list_experiments.py +0 -0
  29. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/list_runs.py +0 -0
  30. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/load_config.py +0 -0
  31. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/load_experiment.py +0 -0
  32. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/models.py +0 -0
  33. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/notebook_kernel.py +0 -0
  34. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/output_html.py +0 -0
  35. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/parse_run_view.py +0 -0
  36. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/pivot_cells.py +0 -0
  37. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/print_rows.py +0 -0
  38. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/python_blocks.py +0 -0
  39. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/read_config.py +0 -0
  40. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/read_path.py +0 -0
  41. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/render_markdown.py +0 -0
  42. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/__init__.py +0 -0
  43. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/describe_event.py +0 -0
  44. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/lock_path.py +0 -0
  45. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/new_run_id.py +0 -0
  46. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/param_args.py +0 -0
  47. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/param_grid.py +0 -0
  48. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/plan_runs.py +0 -0
  49. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/read_experiment_config.py +0 -0
  50. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/run_folder.py +0 -0
  51. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/run_headless.py +0 -0
  52. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/shell_command.py +0 -0
  53. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/start_experiment.py +0 -0
  54. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/stop_then_kill.py +0 -0
  55. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/runner/write_run_json.py +0 -0
  56. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/saved_outputs.py +0 -0
  57. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/serve_app.py +0 -0
  58. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/server.py +0 -0
  59. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/sort_href.py +0 -0
  60. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/sql_quote.py +0 -0
  61. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/static/labnotes.css +0 -0
  62. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/static/row-link.js +0 -0
  63. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/static/run-cell.js +0 -0
  64. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/static/view-toggle.js +0 -0
  65. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/stop_idle_kernels.py +0 -0
  66. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/summarize_config.py +0 -0
  67. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/__init__.py +0 -0
  68. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/experiment_rows.py +0 -0
  69. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/file_prefix.py +0 -0
  70. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/iso_time.py +0 -0
  71. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/list_experiment_runs.py +0 -0
  72. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/open_tables.py +0 -0
  73. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/path_glob.py +0 -0
  74. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/path_pattern.py +0 -0
  75. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/read_experiment.py +0 -0
  76. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/read_file.py +0 -0
  77. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/read_run.py +0 -0
  78. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tables/summarize_experiments.py +0 -0
  79. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/templates/base.html +0 -0
  80. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/templates/experiment.html +0 -0
  81. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/templates/index.html +0 -0
  82. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/templates/macros.html +0 -0
  83. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tui/__init__.py +0 -0
  84. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tui/format_elapsed.py +0 -0
  85. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tui/run_app.py +0 -0
  86. {labnotes-0.0.20 → labnotes-0.0.21}/src/labnotes/tui/run_tui.py +0 -0
  87. {labnotes-0.0.20 → labnotes-0.0.21}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.0.20
3
+ Version: 0.0.21
4
4
  Summary: Browse experiment designs, notes, and run data from a folder.
5
5
  Project-URL: Repository, https://github.com/thekevinscott/labnotes
6
6
  Author-email: Kevin Scott <me@thekevinscott.com>
@@ -73,7 +73,8 @@ A script run by labnotes should:
73
73
 
74
74
  - take each param as a `--name value` flag, parsing lists and tables from JSON;
75
75
  - 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.
76
+ - exit non-zero when it fails, so the run counts as failed and runs again next time;
77
+ - stop any background process it starts before it exits; the run ends when `command` exits.
77
78
 
78
79
  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
80
 
@@ -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())
@@ -32,7 +32,7 @@ async def run_experiment(
32
32
  report({"event": "planned", "runs": len(plan), "concurrency": concurrency})
33
33
  running: set[asyncio.subprocess.Process] = set()
34
34
  results: list[dict[str, object]] = []
35
- stopping = False
35
+ stopping = asyncio.Event()
36
36
 
37
37
  def signal_runs(signal_number: int) -> None:
38
38
  for process in running:
@@ -40,10 +40,9 @@ async def run_experiment(
40
40
  os.killpg(process.pid, signal_number)
41
41
 
42
42
  def interrupt() -> None:
43
- nonlocal stopping
44
43
  plan.clear()
45
- if not stopping:
46
- stopping = True
44
+ if not stopping.is_set():
45
+ stopping.set()
47
46
  report({"event": "stopping"})
48
47
  signal_runs(signal.SIGTERM)
49
48
  elif running:
@@ -60,7 +59,7 @@ async def run_experiment(
60
59
  folder.mkdir(parents=True)
61
60
  report({"event": "started", "run": run, "params": params})
62
61
  result = await execute_run(
63
- config.root, folder, experiment.command, params, running
62
+ config.root, folder, experiment.command, params, running, stopping
64
63
  )
65
64
  report({"event": "ended", "run": run, **result})
66
65
  results.append(result)
@@ -0,0 +1,11 @@
1
+ import asyncio
2
+ import os
3
+
4
+
5
+ async def wait_for_group(group: int) -> None:
6
+ while True:
7
+ try:
8
+ os.killpg(group, 0)
9
+ except ProcessLookupError:
10
+ return
11
+ await asyncio.sleep(0.1)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes