labnotes 0.0.107__tar.gz → 0.0.109__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 (179) hide show
  1. {labnotes-0.0.107 → labnotes-0.0.109}/PKG-INFO +3 -2
  2. {labnotes-0.0.107 → labnotes-0.0.109}/README.md +2 -1
  3. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/context.md +3 -2
  4. labnotes-0.0.109/src/labnotes/daemon/finalize_lost_runs.py +24 -0
  5. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/run_daemon.py +2 -0
  6. labnotes-0.0.109/src/labnotes/utils/process_alive.py +13 -0
  7. {labnotes-0.0.107 → labnotes-0.0.109}/.gitignore +0 -0
  8. {labnotes-0.0.107 → labnotes-0.0.109}/CHANGELOG.md +0 -0
  9. {labnotes-0.0.107 → labnotes-0.0.109}/MIGRATIONS.md +0 -0
  10. {labnotes-0.0.107 → labnotes-0.0.109}/pyproject.toml +0 -0
  11. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/__init__.py +0 -0
  12. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/__main__.py +0 -0
  13. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/asset_url.py +0 -0
  14. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/check_counts.py +0 -0
  15. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/cli.py +0 -0
  16. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/compose_experiment.py +0 -0
  17. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/context_guide.py +0 -0
  18. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/count_groups.py +0 -0
  19. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/count_runs.py +0 -0
  20. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/create_app.py +0 -0
  21. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/create_experiment.py +0 -0
  22. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/create_note.py +0 -0
  23. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/__init__.py +0 -0
  24. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/control_run.py +0 -0
  25. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/daemon_app.py +0 -0
  26. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/daemon_client.py +0 -0
  27. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/daemon_running.py +0 -0
  28. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/daemon_server.py +0 -0
  29. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/describe_status.py +0 -0
  30. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/ensure_daemon.py +0 -0
  31. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/exit_when_idle.py +0 -0
  32. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/remote_start.py +0 -0
  33. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/run_logs.py +0 -0
  34. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/serve_daemon.py +0 -0
  35. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/socket_path.py +0 -0
  36. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/stop_runs.py +0 -0
  37. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/watch_daemon.py +0 -0
  38. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon/watch_status.py +0 -0
  39. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/daemon_command.py +0 -0
  40. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/data_html.py +0 -0
  41. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/default_config_text.py +0 -0
  42. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/default_counts.py +0 -0
  43. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/drain_watch.py +0 -0
  44. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/experiment_notes.py +0 -0
  45. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/experiment_runs.py +0 -0
  46. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/experiments.py +0 -0
  47. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/file_changes.py +0 -0
  48. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/format_duration.py +0 -0
  49. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/format_started.py +0 -0
  50. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/format_value.py +0 -0
  51. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/group_runs.py +0 -0
  52. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/init.py +0 -0
  53. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/invocation_started.py +0 -0
  54. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/is_non_negative_int.py +0 -0
  55. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/is_positive_int.py +0 -0
  56. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/list_experiments.py +0 -0
  57. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/list_runs.py +0 -0
  58. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/load_config.py +0 -0
  59. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/load_experiment.py +0 -0
  60. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/load_run.py +0 -0
  61. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/log.py +0 -0
  62. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/log_path.py +0 -0
  63. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/models.py +0 -0
  64. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/modes.py +0 -0
  65. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/nests_runs.py +0 -0
  66. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/note_path.py +0 -0
  67. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/notebook_kernel.py +0 -0
  68. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/output_html.py +0 -0
  69. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/parse_run_view.py +0 -0
  70. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/print_rows.py +0 -0
  71. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/python_blocks.py +0 -0
  72. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/read_config.py +0 -0
  73. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/read_path.py +0 -0
  74. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/render_markdown.py +0 -0
  75. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/retries.py +0 -0
  76. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/run_command.py +0 -0
  77. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/run_control.py +0 -0
  78. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/run_output.py +0 -0
  79. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/run_status.py +0 -0
  80. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/__init__.py +0 -0
  81. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/describe_event.py +0 -0
  82. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/execute_run.py +0 -0
  83. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/exit_signal.py +0 -0
  84. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/hold_folder.py +0 -0
  85. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/new_param_run_id.py +0 -0
  86. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/new_run_id.py +0 -0
  87. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/new_stamped_run_id.py +0 -0
  88. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/param_args.py +0 -0
  89. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/param_grid.py +0 -0
  90. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/param_run_name.py +0 -0
  91. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/plan_runs.py +0 -0
  92. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/prepare_experiment.py +0 -0
  93. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/read_experiment_config.py +0 -0
  94. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/run_experiment.py +0 -0
  95. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/run_folder.py +0 -0
  96. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/run_headless.py +0 -0
  97. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/runs_parent.py +0 -0
  98. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/shell_command.py +0 -0
  99. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/signal_tree.py +0 -0
  100. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/stop_then_kill.py +0 -0
  101. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/wait_for_tree.py +0 -0
  102. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/runner/write_run_json.py +0 -0
  103. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/save_conflict_copy.py +0 -0
  104. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/save_note.py +0 -0
  105. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/saved_outputs.py +0 -0
  106. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/serve_app.py +0 -0
  107. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/server.py +0 -0
  108. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/shared_params.py +0 -0
  109. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/sort_href.py +0 -0
  110. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/sql_quote.py +0 -0
  111. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/elapsed.js +0 -0
  112. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/experiment.js +0 -0
  113. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/labnotes.css +0 -0
  114. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/local-time.js +0 -0
  115. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/new-note.js +0 -0
  116. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/note-edit.js +0 -0
  117. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/note-editor.js +0 -0
  118. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/row-link.js +0 -0
  119. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/run-cell.js +0 -0
  120. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/run-output.js +0 -0
  121. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/static/view-toggle.js +0 -0
  122. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/summarize_config.py +0 -0
  123. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/__init__.py +0 -0
  124. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/experiment_rows.py +0 -0
  125. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/experiment_run_ids.py +0 -0
  126. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/experiment_toml.py +0 -0
  127. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/experiments_ddl.py +0 -0
  128. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/file_prefix.py +0 -0
  129. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/file_text.py +0 -0
  130. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/iso_time.py +0 -0
  131. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/list_experiment_runs.py +0 -0
  132. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/note_text.py +0 -0
  133. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/open_tables.py +0 -0
  134. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/path_glob.py +0 -0
  135. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/path_pattern.py +0 -0
  136. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/read_blocks.py +0 -0
  137. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/read_experiment.py +0 -0
  138. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/read_file.py +0 -0
  139. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/read_log.py +0 -0
  140. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/read_run.py +0 -0
  141. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tables/summarize_experiments.py +0 -0
  142. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/templates/base.html +0 -0
  143. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/templates/editor_imports.html +0 -0
  144. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/templates/experiment.html +0 -0
  145. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/templates/index.html +0 -0
  146. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/templates/note.html +0 -0
  147. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/templates/run.html +0 -0
  148. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/__init__.py +0 -0
  149. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/browse_app.py +0 -0
  150. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/browse_tui.py +0 -0
  151. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/config.py +0 -0
  152. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/dim.py +0 -0
  153. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/experiment_screen.py +0 -0
  154. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/experiments_screen.py +0 -0
  155. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/fill_table.py +0 -0
  156. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/format_elapsed.py +0 -0
  157. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/group_cells.py +0 -0
  158. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/invocation_columns.py +0 -0
  159. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/invocation_rows.py +0 -0
  160. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/leave_prompt.py +0 -0
  161. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/log_block.py +0 -0
  162. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/log_blocks.py +0 -0
  163. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/reload_on_change.py +0 -0
  164. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/run_app.py +0 -0
  165. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/run_cells.py +0 -0
  166. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/run_output.py +0 -0
  167. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/run_screen.py +0 -0
  168. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/run_tui.py +0 -0
  169. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/runs_table.py +0 -0
  170. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/tui/stopped_title.py +0 -0
  171. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/utils/__init__.py +0 -0
  172. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/utils/format_bytes.py +0 -0
  173. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/utils/run_usage.py +0 -0
  174. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/utils/tree_rss.py +0 -0
  175. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/wait_for_note.py +0 -0
  176. {labnotes-0.0.107 → labnotes-0.0.109}/src/labnotes/watch_rows.py +0 -0
  177. {labnotes-0.0.107 → labnotes-0.0.109}/testing-conventions.toml +0 -0
  178. {labnotes-0.0.107 → labnotes-0.0.109}/tests/integration/conftest.py +0 -0
  179. {labnotes-0.0.107 → labnotes-0.0.109}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.0.107
3
+ Version: 0.0.109
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>
@@ -122,7 +122,8 @@ line per event instead.
122
122
  - With runs going, Ctrl-C asks first. Enter (or a second Ctrl-C) leaves them
123
123
  running. `s` stops the running scripts and records their end, then kills any
124
124
  still going 5 seconds later. Escape goes back. SIGTERM stops them like `s`.
125
- - A second `run` of the same experiment fails while the first holds the lock.
125
+ - A second `run` of the same experiment fails with "already running" while the
126
+ daemon still has the first going.
126
127
  - It exits 0 even if runs fail; the summary line counts failures. If it can't
127
128
  start or reach the daemon, it prints the cause on one line and exits 1.
128
129
 
@@ -98,7 +98,8 @@ line per event instead.
98
98
  - With runs going, Ctrl-C asks first. Enter (or a second Ctrl-C) leaves them
99
99
  running. `s` stops the running scripts and records their end, then kills any
100
100
  still going 5 seconds later. Escape goes back. SIGTERM stops them like `s`.
101
- - A second `run` of the same experiment fails while the first holds the lock.
101
+ - A second `run` of the same experiment fails with "already running" while the
102
+ daemon still has the first going.
102
103
  - It exits 0 even if runs fail; the summary line counts failures. If it can't
103
104
  start or reach the daemon, it prints the cause on one line and exits 1.
104
105
 
@@ -77,8 +77,9 @@ Each param is appended as `--name value`, with the name as written (use
77
77
  and tables as JSON. labnotes writes `run.json` (params, created, ended, exit
78
78
  code, `ended_by`, `signal`), `stdout.log` and `stderr.log` to the
79
79
  run folder. `ended_by` is `"timeout"`, `"stop"` (labnotes stopped it),
80
- `"signal"` (a signal labnotes didn't send, like the out-of-memory killer) or
81
- null (the script exited itself). `signal` is the signal number read from the
80
+ `"signal"` (a signal labnotes didn't send, like the out-of-memory killer),
81
+ `"lost"` (its process died unseen; the daemon marks these when it starts, with
82
+ `ended` set and `exit_code` null) or null (the script exited itself). `signal` is the signal number read from the
82
83
  exit code (negative, or 128 + N from the shell), else null.
83
84
  A run counts when it exited 0 and its `run.json` doesn't hold
84
85
  `"excluded": true`, so excluding a run by hand means adding that key; the next
@@ -0,0 +1,24 @@
1
+ import json
2
+ import time
3
+
4
+ from labnotes.list_runs import UNFINISHED
5
+ from labnotes.models import LabnotesConfig
6
+ from labnotes.runner.write_run_json import write_run_json
7
+ from labnotes.tables.iso_time import iso_time
8
+ from labnotes.tables.open_tables import open_tables
9
+ from labnotes.utils.process_alive import process_alive
10
+
11
+
12
+ async def finalize_lost_runs(config: LabnotesConfig) -> None:
13
+ rows = await open_tables(config).query(
14
+ f"SELECT folder, run FROM runs WHERE {UNFINISHED}"
15
+ )
16
+ for row in rows:
17
+ run = json.loads(str(row["run"]))
18
+ pid, created = run.get("pid"), run.get("pid_created")
19
+ if isinstance(pid, int) and process_alive(
20
+ pid, created if isinstance(created, int | float) else None
21
+ ):
22
+ continue
23
+ run |= {"ended": iso_time(time.time()), "ended_by": "lost"}
24
+ write_run_json(config.root / str(row["folder"]), run)
@@ -6,11 +6,13 @@ import uvicorn
6
6
  from labnotes.daemon.daemon_app import daemon_app
7
7
  from labnotes.daemon.daemon_server import DaemonServer
8
8
  from labnotes.daemon.exit_when_idle import exit_when_idle
9
+ from labnotes.daemon.finalize_lost_runs import finalize_lost_runs
9
10
  from labnotes.daemon.socket_path import socket_path
10
11
  from labnotes.models import LabnotesConfig
11
12
 
12
13
 
13
14
  async def run_daemon(config: LabnotesConfig) -> None:
15
+ await finalize_lost_runs(config)
14
16
  path = socket_path(config.root)
15
17
  app = daemon_app(config)
16
18
  server = DaemonServer(
@@ -0,0 +1,13 @@
1
+ import psutil
2
+
3
+ from labnotes.utils.tree_rss import SAME_START_SECONDS
4
+
5
+
6
+ def process_alive(pid: int, created: float | None) -> bool:
7
+ try:
8
+ started = psutil.Process(pid).create_time()
9
+ except psutil.NoSuchProcess:
10
+ return False
11
+ except psutil.AccessDenied:
12
+ return True
13
+ return created is None or abs(started - created) <= SAME_START_SECONDS
File without changes
File without changes
File without changes
File without changes
File without changes