labnotes 0.0.38__tar.gz → 0.0.40__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 (94) hide show
  1. {labnotes-0.0.38 → labnotes-0.0.40}/PKG-INFO +1 -1
  2. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/context.md +1 -1
  3. labnotes-0.0.40/src/labnotes/runner/new_stamped_run_id.py +9 -0
  4. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/run_experiment.py +3 -3
  5. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tui/run_app.py +14 -9
  6. {labnotes-0.0.38 → labnotes-0.0.40}/.gitignore +0 -0
  7. {labnotes-0.0.38 → labnotes-0.0.40}/CHANGELOG.md +0 -0
  8. {labnotes-0.0.38 → labnotes-0.0.40}/MIGRATIONS.md +0 -0
  9. {labnotes-0.0.38 → labnotes-0.0.40}/README.md +0 -0
  10. {labnotes-0.0.38 → labnotes-0.0.40}/pyproject.toml +0 -0
  11. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/__init__.py +0 -0
  12. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/asset_url.py +0 -0
  13. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/cell_progress.py +0 -0
  14. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/check_counts.py +0 -0
  15. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/cli.py +0 -0
  16. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/context_guide.py +0 -0
  17. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/count_runs.py +0 -0
  18. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/create_app.py +0 -0
  19. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/create_experiment.py +0 -0
  20. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/data_html.py +0 -0
  21. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/default_config_text.py +0 -0
  22. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/default_counts.py +0 -0
  23. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/drain_watch.py +0 -0
  24. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/experiment_notes.py +0 -0
  25. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/experiment_progress.py +0 -0
  26. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/experiment_runs.py +0 -0
  27. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/experiments.py +0 -0
  28. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/format_value.py +0 -0
  29. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/init.py +0 -0
  30. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/list_experiments.py +0 -0
  31. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/list_runs.py +0 -0
  32. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/load_config.py +0 -0
  33. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/load_experiment.py +0 -0
  34. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/models.py +0 -0
  35. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/notebook_kernel.py +0 -0
  36. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/output_html.py +0 -0
  37. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/parse_run_view.py +0 -0
  38. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/pivot_cells.py +0 -0
  39. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/print_rows.py +0 -0
  40. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/python_blocks.py +0 -0
  41. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/read_config.py +0 -0
  42. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/read_path.py +0 -0
  43. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/render_markdown.py +0 -0
  44. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/__init__.py +0 -0
  45. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/describe_event.py +0 -0
  46. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/execute_run.py +0 -0
  47. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/hold_folder.py +0 -0
  48. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/new_invocation_id.py +0 -0
  49. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/new_run_id.py +0 -0
  50. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/param_args.py +0 -0
  51. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/param_grid.py +0 -0
  52. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/plan_runs.py +0 -0
  53. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/read_experiment_config.py +0 -0
  54. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/run_folder.py +0 -0
  55. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/run_headless.py +0 -0
  56. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/runs_parent.py +0 -0
  57. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/shell_command.py +0 -0
  58. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/start_experiment.py +0 -0
  59. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/stop_then_kill.py +0 -0
  60. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/wait_for_group.py +0 -0
  61. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/runner/write_run_json.py +0 -0
  62. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/saved_outputs.py +0 -0
  63. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/serve_app.py +0 -0
  64. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/server.py +0 -0
  65. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/sort_href.py +0 -0
  66. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/sql_quote.py +0 -0
  67. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/static/labnotes.css +0 -0
  68. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/static/row-link.js +0 -0
  69. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/static/run-cell.js +0 -0
  70. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/static/view-toggle.js +0 -0
  71. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/stop_idle_kernels.py +0 -0
  72. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/summarize_config.py +0 -0
  73. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/__init__.py +0 -0
  74. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/experiment_rows.py +0 -0
  75. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/file_prefix.py +0 -0
  76. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/iso_time.py +0 -0
  77. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/list_experiment_runs.py +0 -0
  78. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/open_tables.py +0 -0
  79. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/path_glob.py +0 -0
  80. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/path_pattern.py +0 -0
  81. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/read_experiment.py +0 -0
  82. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/read_file.py +0 -0
  83. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/read_run.py +0 -0
  84. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tables/summarize_experiments.py +0 -0
  85. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/templates/base.html +0 -0
  86. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/templates/experiment.html +0 -0
  87. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/templates/index.html +0 -0
  88. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/templates/macros.html +0 -0
  89. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tui/__init__.py +0 -0
  90. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tui/dim.py +0 -0
  91. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tui/format_elapsed.py +0 -0
  92. {labnotes-0.0.38 → labnotes-0.0.40}/src/labnotes/tui/run_tui.py +0 -0
  93. {labnotes-0.0.38 → labnotes-0.0.40}/testing-conventions.toml +0 -0
  94. {labnotes-0.0.38 → labnotes-0.0.40}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.0.38
3
+ Version: 0.0.40
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>
@@ -61,7 +61,7 @@ that count fill a combination, so running again fills in what failed or never
61
61
  ran.
62
62
  `mode = "adhoc"` skips that: every `run` runs each combination `repeats` times
63
63
  again, whatever is already on disk. The default is `mode = "fill"`.
64
- Each run gets a new run folder. `command` runs in a shell from the root, with
64
+ Each run gets a new run folder, named for the UTC time it was planned (`20261020-000429-brave-fox`), so folder listings sort by start. `command` runs in a shell from the root, with
65
65
  `{run_dir}` replaced by the run folder's absolute path. Pass it to the
66
66
  script so it writes its outputs there.
67
67
  Each param is appended as `--name value`, with the name as written (use
@@ -0,0 +1,9 @@
1
+ from collections.abc import Callable
2
+ from datetime import UTC, datetime
3
+
4
+ from labnotes.runner.new_run_id import new_run_id
5
+
6
+
7
+ def new_stamped_run_id(taken: Callable[[str], bool]) -> str:
8
+ stamp = datetime.now(UTC).strftime("%Y%m%d-%H%M%S")
9
+ return f"{stamp}-" + new_run_id(lambda name: taken(f"{stamp}-{name}"))
@@ -13,7 +13,7 @@ from labnotes.models import (
13
13
  )
14
14
  from labnotes.runner.execute_run import execute_run
15
15
  from labnotes.runner.new_invocation_id import new_invocation_id
16
- from labnotes.runner.new_run_id import new_run_id
16
+ from labnotes.runner.new_stamped_run_id import new_stamped_run_id
17
17
  from labnotes.runner.param_grid import param_grid
18
18
  from labnotes.runner.plan_runs import plan_runs
19
19
  from labnotes.runner.run_folder import run_folder
@@ -49,7 +49,7 @@ async def run_experiment(
49
49
  for params in plan_runs(
50
50
  param_grid(experiment.params), experiment.repeats, finished
51
51
  ):
52
- run = new_run_id(taken)
52
+ run = new_stamped_run_id(taken)
53
53
  planned.add(run)
54
54
  plan.append((run, params))
55
55
  runs = [{"run": run, "params": params} for run, params in plan]
@@ -131,7 +131,7 @@ async def run_experiment(
131
131
  def retry(run: str) -> None:
132
132
  if stopping.is_set() or run not in ended:
133
133
  return
134
- again = new_run_id(taken)
134
+ again = new_stamped_run_id(taken)
135
135
  planned.add(again)
136
136
  plan.append((again, ended[run]))
137
137
  report({"event": "retried", "run": run, "retry": again, "params": ended[run]})
@@ -17,6 +17,8 @@ from labnotes.tui.format_elapsed import format_elapsed
17
17
 
18
18
  COLUMNS = ("run", "params", "status", "time")
19
19
  OTHER_STREAM = {"stdout": "stderr", "stderr": "stdout"}
20
+ PENDING, RUNNING, PASSED, FAILED = "○", "◐", "✓", "✗"
21
+ SKIPPED, DESELECTED = "–", "⊘"
20
22
 
21
23
 
22
24
  class RunApp(App[None]):
@@ -101,7 +103,7 @@ class RunApp(App[None]):
101
103
  case "planned":
102
104
  for planned in event["runs"]: # type: ignore[attr-defined]
103
105
  args = " ".join(param_args(planned["params"]))
104
- cells = (planned["run"], args, "pending", "")
106
+ cells = (planned["run"], args, PENDING, "")
105
107
  self.cells[planned["run"]] = [*map(dim, cells)]
106
108
  self.pending.add(planned["run"])
107
109
  self.redraw(table.cursor_row)
@@ -118,13 +120,16 @@ class RunApp(App[None]):
118
120
  if run == self.next_run:
119
121
  self.show("")
120
122
  args = " ".join(param_args(event["params"])) # type: ignore[arg-type]
121
- cells = (run, args, "running", format_elapsed(0))
123
+ cells = (run, args, RUNNING, format_elapsed(0))
122
124
  for column, cell in zip(COLUMNS, cells):
123
125
  self.set_cell(run, column, cell)
124
126
  case "ended":
125
- outcome = event["error"] or f"exited {event['exit_code']}"
126
- style = "red" if event["error"] or event["exit_code"] else ""
127
- if style:
127
+ outcome = PASSED
128
+ style = ""
129
+ if event["error"] or event["exit_code"]:
130
+ reason = event["error"] or f"exited {event['exit_code']}"
131
+ outcome = f"{FAILED} {reason}"
132
+ style = "red"
128
133
  self.failed.add(run)
129
134
  self.ended.add(run)
130
135
  self.set_cell(run, "status", Text(str(outcome), style=style))
@@ -138,18 +143,18 @@ class RunApp(App[None]):
138
143
  self.next_run = run
139
144
  case "deselected":
140
145
  self.pending.discard(run)
141
- self.set_cell(run, "status", dim("deselected"))
146
+ self.set_cell(run, "status", dim(DESELECTED))
142
147
  self.reorder(event, table.cursor_row)
143
148
  self.show(describe_event(event))
144
149
  case "reselected":
145
150
  self.pending.add(run)
146
- self.set_cell(run, "status", dim("pending"))
151
+ self.set_cell(run, "status", dim(PENDING))
147
152
  self.reorder(event, table.cursor_row)
148
153
  self.show(describe_event(event))
149
154
  case "retried":
150
155
  again = str(event["retry"])
151
156
  args = " ".join(param_args(event["params"])) # type: ignore[arg-type]
152
- self.cells[again] = [*map(dim, (again, args, "pending", ""))]
157
+ self.cells[again] = [*map(dim, (again, args, PENDING, ""))]
153
158
  self.pending.add(again)
154
159
  self.redraw(table.cursor_row)
155
160
  self.show(describe_event(event))
@@ -161,7 +166,7 @@ class RunApp(App[None]):
161
166
  self.show(describe_event(event))
162
167
  case "stopping":
163
168
  for pending in self.pending:
164
- self.set_cell(pending, "status", dim("skipped"))
169
+ self.set_cell(pending, "status", dim(SKIPPED))
165
170
  self.show(describe_event(event))
166
171
  case _:
167
172
  self.show(describe_event(event))
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes