labnotes 0.0.41__tar.gz → 0.0.43__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.41 → labnotes-0.0.43}/PKG-INFO +1 -1
  2. {labnotes-0.0.41 → labnotes-0.0.43}/pyproject.toml +1 -0
  3. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/context.md +1 -1
  4. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tui/run_app.py +10 -2
  5. {labnotes-0.0.41 → labnotes-0.0.43}/uv.lock +27 -0
  6. {labnotes-0.0.41 → labnotes-0.0.43}/.gitignore +0 -0
  7. {labnotes-0.0.41 → labnotes-0.0.43}/CHANGELOG.md +0 -0
  8. {labnotes-0.0.41 → labnotes-0.0.43}/MIGRATIONS.md +0 -0
  9. {labnotes-0.0.41 → labnotes-0.0.43}/README.md +0 -0
  10. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/__init__.py +0 -0
  11. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/asset_url.py +0 -0
  12. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/cell_progress.py +0 -0
  13. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/check_counts.py +0 -0
  14. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/cli.py +0 -0
  15. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/context_guide.py +0 -0
  16. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/count_runs.py +0 -0
  17. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/create_app.py +0 -0
  18. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/create_experiment.py +0 -0
  19. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/data_html.py +0 -0
  20. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/default_config_text.py +0 -0
  21. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/default_counts.py +0 -0
  22. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/drain_watch.py +0 -0
  23. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/experiment_notes.py +0 -0
  24. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/experiment_progress.py +0 -0
  25. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/experiment_runs.py +0 -0
  26. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/experiments.py +0 -0
  27. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/format_value.py +0 -0
  28. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/init.py +0 -0
  29. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/list_experiments.py +0 -0
  30. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/list_runs.py +0 -0
  31. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/load_config.py +0 -0
  32. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/load_experiment.py +0 -0
  33. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/models.py +0 -0
  34. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/notebook_kernel.py +0 -0
  35. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/output_html.py +0 -0
  36. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/parse_run_view.py +0 -0
  37. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/pivot_cells.py +0 -0
  38. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/print_rows.py +0 -0
  39. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/python_blocks.py +0 -0
  40. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/read_config.py +0 -0
  41. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/read_path.py +0 -0
  42. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/render_markdown.py +0 -0
  43. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/__init__.py +0 -0
  44. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/describe_event.py +0 -0
  45. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/execute_run.py +0 -0
  46. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/hold_folder.py +0 -0
  47. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/new_invocation_id.py +0 -0
  48. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/new_run_id.py +0 -0
  49. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/new_stamped_run_id.py +0 -0
  50. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/param_args.py +0 -0
  51. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/param_grid.py +0 -0
  52. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/plan_runs.py +0 -0
  53. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/read_experiment_config.py +0 -0
  54. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/run_experiment.py +0 -0
  55. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/run_folder.py +0 -0
  56. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/run_headless.py +0 -0
  57. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/runs_parent.py +0 -0
  58. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/shell_command.py +0 -0
  59. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/start_experiment.py +0 -0
  60. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/stop_then_kill.py +0 -0
  61. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/wait_for_group.py +0 -0
  62. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/runner/write_run_json.py +0 -0
  63. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/saved_outputs.py +0 -0
  64. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/serve_app.py +0 -0
  65. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/server.py +0 -0
  66. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/sort_href.py +0 -0
  67. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/sql_quote.py +0 -0
  68. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/static/labnotes.css +0 -0
  69. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/static/row-link.js +0 -0
  70. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/static/run-cell.js +0 -0
  71. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/static/view-toggle.js +0 -0
  72. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/stop_idle_kernels.py +0 -0
  73. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/summarize_config.py +0 -0
  74. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/__init__.py +0 -0
  75. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/experiment_rows.py +0 -0
  76. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/file_prefix.py +0 -0
  77. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/iso_time.py +0 -0
  78. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/list_experiment_runs.py +0 -0
  79. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/open_tables.py +0 -0
  80. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/path_glob.py +0 -0
  81. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/path_pattern.py +0 -0
  82. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/read_experiment.py +0 -0
  83. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/read_file.py +0 -0
  84. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/read_run.py +0 -0
  85. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tables/summarize_experiments.py +0 -0
  86. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/templates/base.html +0 -0
  87. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/templates/experiment.html +0 -0
  88. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/templates/index.html +0 -0
  89. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/templates/macros.html +0 -0
  90. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tui/__init__.py +0 -0
  91. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tui/dim.py +0 -0
  92. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tui/format_elapsed.py +0 -0
  93. {labnotes-0.0.41 → labnotes-0.0.43}/src/labnotes/tui/run_tui.py +0 -0
  94. {labnotes-0.0.41 → labnotes-0.0.43}/testing-conventions.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.0.41
3
+ Version: 0.0.43
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>
@@ -32,6 +32,7 @@ labnotes = "labnotes.cli:main"
32
32
 
33
33
  [dependency-groups]
34
34
  dev = [
35
+ "curtaincall>=0.3.0",
35
36
  "httpx2>=2.13.1",
36
37
  "mypy>=2.4.0",
37
38
  "pytest>=8.0",
@@ -56,7 +56,7 @@ effort = ["low", "high"]
56
56
  `name` is optional: a display name for the experiment, shown in the live view
57
57
  and as `display_name` in `experiments`. The folder name stays the key, so
58
58
  renaming the experiment means editing `name`.
59
- Each combination of `[params]` runs `repeats` times, round-robin, on every
59
+ Each combination of `[params]` runs `repeats` times (1 if omitted), round-robin, on every
60
60
  `run`, whatever is already on disk. That is `mode = "adhoc"`, the default.
61
61
  `mode = "fill"` counts what is there: only runs that count fill a combination,
62
62
  so running again fills in what failed or never ran.
@@ -17,7 +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 = "○", "◐", "✓", "✗"
20
+ PENDING, PASSED, FAILED = "○", "✓", "✗"
21
+ SPINNER = "|/-\\"
21
22
  SKIPPED, DESELECTED = "–", "⊘"
22
23
 
23
24
 
@@ -56,6 +57,7 @@ class RunApp(App[None]):
56
57
  self.idle = False
57
58
  self.next_run: str | None = None
58
59
  self.ended: set[str] = set()
60
+ self.frame = 0
59
61
  self.started: dict[str, float] = {}
60
62
  self.pending: set[str] = set()
61
63
  self.cells: dict[str, list[object]] = {}
@@ -82,6 +84,7 @@ class RunApp(App[None]):
82
84
  table.add_column(column, key=column)
83
85
  self.query_one(Input).display = False
84
86
  self.query_one(Log).display = False
87
+ table.set_interval(0.1, self.spin)
85
88
 
86
89
  def on_ready(self) -> None:
87
90
  self.set_interval(1, self.tick)
@@ -120,7 +123,7 @@ class RunApp(App[None]):
120
123
  if run == self.next_run:
121
124
  self.show("")
122
125
  args = " ".join(param_args(event["params"])) # type: ignore[arg-type]
123
- cells = (run, args, RUNNING, format_elapsed(0))
126
+ cells = (run, args, SPINNER[self.frame], format_elapsed(0))
124
127
  for column, cell in zip(COLUMNS, cells):
125
128
  self.set_cell(run, column, cell)
126
129
  case "ended":
@@ -205,6 +208,11 @@ class RunApp(App[None]):
205
208
  for run, began in self.started.items():
206
209
  self.set_cell(run, "time", format_elapsed(time.monotonic() - began))
207
210
 
211
+ def spin(self) -> None:
212
+ self.frame = (self.frame + 1) % len(SPINNER)
213
+ for run in self.started:
214
+ self.set_cell(run, "status", SPINNER[self.frame])
215
+
208
216
  def show(self, text: str) -> None:
209
217
  self.next_run = None
210
218
  self.query_one("#status", Static).update(text)
@@ -223,6 +223,19 @@ wheels = [
223
223
  { url = "https://files.pythonhosted.org/packages/60/97/891a0971e1e4a8c5d2b20bbe0e524dc04548d2307fee33cdeba148fd4fc7/comm-0.2.3-py3-none-any.whl", hash = "sha256:c615d91d75f7f04f095b30d1c1711babd43bdc6419c1be9886a85f2f4e489417", size = 7294, upload-time = "2025-07-25T14:02:02.896Z" },
224
224
  ]
225
225
 
226
+ [[package]]
227
+ name = "curtaincall"
228
+ version = "0.3.0"
229
+ source = { registry = "https://pypi.org/simple" }
230
+ dependencies = [
231
+ { name = "pexpect" },
232
+ { name = "pyte" },
233
+ ]
234
+ sdist = { url = "https://files.pythonhosted.org/packages/ca/12/056a3c89fcbc135b15a7cef872472a73bdad3594ddfccbe11b9a028b2f5f/curtaincall-0.3.0.tar.gz", hash = "sha256:afc9e9ce00a256982d69a737976527d38217082ca79fcafcf381fa2cd2bba041", size = 68784, upload-time = "2026-02-16T01:44:39.874Z" }
235
+ wheels = [
236
+ { url = "https://files.pythonhosted.org/packages/f8/52/1092ca83246b88a880f2fe35e38919b9e069c0ce97708880c4234ab72c32/curtaincall-0.3.0-py3-none-any.whl", hash = "sha256:4a754fa4a8ad726a00ec24ebb545f8f5a34a0f8d159936845d125bfeb2dfb652", size = 21504, upload-time = "2026-02-16T01:44:38.567Z" },
237
+ ]
238
+
226
239
  [[package]]
227
240
  name = "debugpy"
228
241
  version = "1.8.22"
@@ -477,6 +490,7 @@ dependencies = [
477
490
 
478
491
  [package.dev-dependencies]
479
492
  dev = [
493
+ { name = "curtaincall" },
480
494
  { name = "httpx2" },
481
495
  { name = "mypy" },
482
496
  { name = "pytest" },
@@ -500,6 +514,7 @@ requires-dist = [
500
514
 
501
515
  [package.metadata.requires-dev]
502
516
  dev = [
517
+ { name = "curtaincall", specifier = ">=0.3.0" },
503
518
  { name = "httpx2", specifier = ">=2.13.1" },
504
519
  { name = "mypy", specifier = ">=2.4.0" },
505
520
  { name = "pytest", specifier = ">=8.0" },
@@ -984,6 +999,18 @@ wheels = [
984
999
  { url = "https://files.pythonhosted.org/packages/71/46/17f022dd3e953bf20a04a028a21ec746d942f8d2af30fa0f124fa0e6a684/pygments-2.21.0-py3-none-any.whl", hash = "sha256:2363c69b61c4a97c838da3b130dcd6468f4848992b21a82f2a63ec34377137d9", size = 1250147, upload-time = "2026-08-17T08:02:44.912Z" },
985
1000
  ]
986
1001
 
1002
+ [[package]]
1003
+ name = "pyte"
1004
+ version = "0.8.2"
1005
+ source = { registry = "https://pypi.org/simple" }
1006
+ dependencies = [
1007
+ { name = "wcwidth" },
1008
+ ]
1009
+ sdist = { url = "https://files.pythonhosted.org/packages/ab/ab/b599762933eba04de7dc5b31ae083112a6c9a9db15b01d3109ad797559d9/pyte-0.8.2.tar.gz", hash = "sha256:5af970e843fa96a97149d64e170c984721f20e52227a2f57f0a54207f08f083f", size = 92301, upload-time = "2023-11-12T09:33:43.217Z" }
1010
+ wheels = [
1011
+ { url = "https://files.pythonhosted.org/packages/59/d0/bb522283b90853afbf506cd5b71c650cf708829914efd0003d615cf426cd/pyte-0.8.2-py3-none-any.whl", hash = "sha256:85db42a35798a5aafa96ac4d8da78b090b2c933248819157fc0e6f78876a0135", size = 31627, upload-time = "2023-11-12T09:33:41.096Z" },
1012
+ ]
1013
+
987
1014
  [[package]]
988
1015
  name = "pytest"
989
1016
  version = "9.1.1"
File without changes
File without changes
File without changes
File without changes
File without changes