labnotes 0.0.126__tar.gz → 0.0.127__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 (213) hide show
  1. {labnotes-0.0.126 → labnotes-0.0.127}/PKG-INFO +1 -1
  2. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/context.md +4 -1
  3. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/log_path.py +1 -1
  4. labnotes-0.0.127/src/labnotes/runner/cgroup_memory.py +17 -0
  5. labnotes-0.0.127/src/labnotes/runner/container_cgroup.py +15 -0
  6. labnotes-0.0.127/src/labnotes/runner/container_memory.py +100 -0
  7. labnotes-0.0.127/src/labnotes/runner/docker_output.py +11 -0
  8. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/execute_run.py +2 -1
  9. labnotes-0.0.127/src/labnotes/runner/record_memory.py +28 -0
  10. labnotes-0.0.126/src/labnotes/runner/record_memory.py +0 -18
  11. {labnotes-0.0.126 → labnotes-0.0.127}/.gitignore +0 -0
  12. {labnotes-0.0.126 → labnotes-0.0.127}/CHANGELOG.md +0 -0
  13. {labnotes-0.0.126 → labnotes-0.0.127}/MIGRATIONS.md +0 -0
  14. {labnotes-0.0.126 → labnotes-0.0.127}/README.md +0 -0
  15. {labnotes-0.0.126 → labnotes-0.0.127}/pyproject.toml +0 -0
  16. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/__init__.py +0 -0
  17. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/__main__.py +0 -0
  18. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/asset_url.py +0 -0
  19. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/check_counts.py +0 -0
  20. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/cli.py +0 -0
  21. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/compose_experiment.py +0 -0
  22. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/context_guide.py +0 -0
  23. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/count_groups.py +0 -0
  24. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/count_runs.py +0 -0
  25. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/create_app.py +0 -0
  26. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/create_experiment.py +0 -0
  27. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/create_note.py +0 -0
  28. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/__init__.py +0 -0
  29. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/control_run.py +0 -0
  30. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/daemon_app.py +0 -0
  31. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/daemon_client.py +0 -0
  32. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/daemon_running.py +0 -0
  33. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/daemon_server.py +0 -0
  34. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/describe_status.py +0 -0
  35. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/ensure_daemon.py +0 -0
  36. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/exit_when_idle.py +0 -0
  37. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/finalize_lost_runs.py +0 -0
  38. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/last_stderr.py +0 -0
  39. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/remote_start.py +0 -0
  40. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/run_daemon.py +0 -0
  41. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/run_logs.py +0 -0
  42. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/serve_daemon.py +0 -0
  43. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/socket_path.py +0 -0
  44. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/stop_runs.py +0 -0
  45. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/watch_daemon.py +0 -0
  46. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon/watch_status.py +0 -0
  47. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/daemon_command.py +0 -0
  48. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/data_html.py +0 -0
  49. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/default_config_text.py +0 -0
  50. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/default_counts.py +0 -0
  51. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/drain_watch.py +0 -0
  52. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/experiment_notes.py +0 -0
  53. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/experiment_runs.py +0 -0
  54. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/experiments.py +0 -0
  55. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/file_changes.py +0 -0
  56. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/format_duration.py +0 -0
  57. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/format_started.py +0 -0
  58. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/format_value.py +0 -0
  59. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/grid_cell.py +0 -0
  60. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/group_runs.py +0 -0
  61. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/init.py +0 -0
  62. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/invocation_started.py +0 -0
  63. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/is_non_negative_int.py +0 -0
  64. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/is_non_negative_number.py +0 -0
  65. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/is_positive_int.py +0 -0
  66. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/list_experiments.py +0 -0
  67. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/list_runs.py +0 -0
  68. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/load_config.py +0 -0
  69. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/load_experiment.py +0 -0
  70. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/load_run.py +0 -0
  71. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/log.py +0 -0
  72. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/memory_interval.py +0 -0
  73. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/models.py +0 -0
  74. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/modes.py +0 -0
  75. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/nests_runs.py +0 -0
  76. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/note_path.py +0 -0
  77. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/notebook_kernel.py +0 -0
  78. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/output_html.py +0 -0
  79. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/parse_run_view.py +0 -0
  80. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/parse_subset.py +0 -0
  81. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/pivot_runs.py +0 -0
  82. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/print_rows.py +0 -0
  83. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/python_blocks.py +0 -0
  84. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/read_config.py +0 -0
  85. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/read_path.py +0 -0
  86. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/render_markdown.py +0 -0
  87. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/retries.py +0 -0
  88. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/run_command.py +0 -0
  89. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/run_control.py +0 -0
  90. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/run_output.py +0 -0
  91. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/run_status.py +0 -0
  92. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/__init__.py +0 -0
  93. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/check_args.py +0 -0
  94. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/check_subset.py +0 -0
  95. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/describe_event.py +0 -0
  96. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/exit_signal.py +0 -0
  97. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/flag_name.py +0 -0
  98. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/hold_folder.py +0 -0
  99. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/in_subset.py +0 -0
  100. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/invocation_hook.py +0 -0
  101. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/invocation_meta.py +0 -0
  102. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/new_param_run_id.py +0 -0
  103. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/new_run_id.py +0 -0
  104. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/new_stamped_run_id.py +0 -0
  105. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/param_args.py +0 -0
  106. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/param_grid.py +0 -0
  107. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/param_run_name.py +0 -0
  108. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/plan_runs.py +0 -0
  109. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/prepare_experiment.py +0 -0
  110. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/read_experiment_config.py +0 -0
  111. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/run_experiment.py +0 -0
  112. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/run_folder.py +0 -0
  113. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/run_headless.py +0 -0
  114. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/runs_parent.py +0 -0
  115. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/shell_command.py +0 -0
  116. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/signal_tree.py +0 -0
  117. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/stop_then_kill.py +0 -0
  118. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/wait_for_tree.py +0 -0
  119. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runner/write_run_json.py +0 -0
  120. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/runs_view_command.py +0 -0
  121. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/save_conflict_copy.py +0 -0
  122. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/save_note.py +0 -0
  123. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/saved_outputs.py +0 -0
  124. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/serve_app.py +0 -0
  125. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/server.py +0 -0
  126. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/shared_params.py +0 -0
  127. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/sort_href.py +0 -0
  128. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/sql_quote.py +0 -0
  129. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/elapsed.js +0 -0
  130. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/experiment.js +0 -0
  131. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/labnotes.css +0 -0
  132. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/local-time.js +0 -0
  133. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/new-note.js +0 -0
  134. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/note-edit.js +0 -0
  135. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/note-editor.js +0 -0
  136. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/row-link.js +0 -0
  137. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/run-cell.js +0 -0
  138. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/run-output.js +0 -0
  139. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/static/view-toggle.js +0 -0
  140. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/summarize_config.py +0 -0
  141. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/__init__.py +0 -0
  142. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/experiment_rows.py +0 -0
  143. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/experiment_run_ids.py +0 -0
  144. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/experiment_toml.py +0 -0
  145. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/experiments_ddl.py +0 -0
  146. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/file_prefix.py +0 -0
  147. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/file_text.py +0 -0
  148. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/iso_time.py +0 -0
  149. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/list_experiment_runs.py +0 -0
  150. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/note_text.py +0 -0
  151. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/open_tables.py +0 -0
  152. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/path_glob.py +0 -0
  153. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/path_pattern.py +0 -0
  154. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/read_blocks.py +0 -0
  155. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/read_experiment.py +0 -0
  156. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/read_file.py +0 -0
  157. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/read_log.py +0 -0
  158. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/read_memory.py +0 -0
  159. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/read_run.py +0 -0
  160. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/read_run_file.py +0 -0
  161. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/runs_columns.py +0 -0
  162. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tables/summarize_experiments.py +0 -0
  163. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/templates/base.html +0 -0
  164. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/templates/editor_imports.html +0 -0
  165. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/templates/experiment.html +0 -0
  166. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/templates/index.html +0 -0
  167. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/templates/note.html +0 -0
  168. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/templates/run.html +0 -0
  169. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/__init__.py +0 -0
  170. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/browse_app.py +0 -0
  171. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/browse_tui.py +0 -0
  172. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/check_view_response.py +0 -0
  173. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/config.py +0 -0
  174. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/dim.py +0 -0
  175. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/experiment_screen.py +0 -0
  176. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/experiments_screen.py +0 -0
  177. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/fill_table.py +0 -0
  178. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/format_cell.py +0 -0
  179. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/format_elapsed.py +0 -0
  180. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/group_cells.py +0 -0
  181. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/invocation_columns.py +0 -0
  182. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/invocation_rows.py +0 -0
  183. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/leave_prompt.py +0 -0
  184. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/log_block.py +0 -0
  185. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/log_blocks.py +0 -0
  186. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/native_rows.py +0 -0
  187. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/reload_on_change.py +0 -0
  188. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/run_app.py +0 -0
  189. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/run_cells.py +0 -0
  190. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/run_output.py +0 -0
  191. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/run_screen.py +0 -0
  192. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/run_tui.py +0 -0
  193. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/runs_table.py +0 -0
  194. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/runs_view.py +0 -0
  195. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/stopped_title.py +0 -0
  196. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/view_column.py +0 -0
  197. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/tui/view_process.py +0 -0
  198. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/utils/__init__.py +0 -0
  199. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/utils/format_bytes.py +0 -0
  200. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/utils/process_alive.py +0 -0
  201. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/utils/run_usage.py +0 -0
  202. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/utils/tree_rss.py +0 -0
  203. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/utils/tree_usage.py +0 -0
  204. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/view/__init__.py +0 -0
  205. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/view/answer.py +0 -0
  206. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/view/build.py +0 -0
  207. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/view/respond.py +0 -0
  208. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/view/serve.py +0 -0
  209. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/wait_for_note.py +0 -0
  210. {labnotes-0.0.126 → labnotes-0.0.127}/src/labnotes/watch_rows.py +0 -0
  211. {labnotes-0.0.126 → labnotes-0.0.127}/testing-conventions.toml +0 -0
  212. {labnotes-0.0.126 → labnotes-0.0.127}/tests/integration/conftest.py +0 -0
  213. {labnotes-0.0.126 → labnotes-0.0.127}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.0.126
3
+ Version: 0.0.127
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>
@@ -130,7 +130,10 @@ Runs can take a long time. Start `run` in the background, then check on it with
130
130
 
131
131
  While a run goes, labnotes appends a memory sample to `memory.jsonl` in its run
132
132
  folder every 5 seconds: one JSON line, `{"time": <unix seconds>, "rss": <bytes>,
133
- "processes": <count>}`, for the command and everything it started. It keeps
133
+ "processes": <count>}`, for the command's process tree and its labeled Docker
134
+ containers. `LABNOTES_RUN` is the absolute run folder path; scripts label containers
135
+ with `docker run --label "labnotes.run=$LABNOTES_RUN" ...` to count their memory.
136
+ `processes` counts host processes in the run's tree, not containers. It keeps
134
137
  every sample, so a run killed for memory still shows the climb.
135
138
  `memory_interval` in `experiment.toml` (or `[experiment]` in `.labnotes.toml`)
136
139
  sets the seconds between samples; 0 turns it off.
@@ -1,8 +1,8 @@
1
1
  import os
2
2
  from pathlib import Path
3
3
 
4
- # The one env contract between runner and script; config is otherwise never env.
5
4
  LOG_ENV = "LABNOTES_LOG"
5
+ RUN_ENV = "LABNOTES_RUN"
6
6
 
7
7
 
8
8
  def log_path() -> Path | None:
@@ -0,0 +1,17 @@
1
+ from pathlib import Path
2
+
3
+
4
+ def cgroup_memory(path: Path) -> int:
5
+ current = int((path / "memory.current").read_text())
6
+ try:
7
+ stats = (path / "memory.stat").read_text().splitlines()
8
+ except OSError:
9
+ return current
10
+ for line in stats:
11
+ name, _, value = line.partition(" ")
12
+ if name == "inactive_file":
13
+ try:
14
+ return max(0, current - int(value))
15
+ except ValueError:
16
+ return current
17
+ return current
@@ -0,0 +1,15 @@
1
+ from pathlib import Path
2
+
3
+ CGROUP_ROOT = Path("/sys/fs/cgroup")
4
+ PROC_ROOT = Path("/proc")
5
+
6
+
7
+ def container_cgroup(pid: int) -> Path | None:
8
+ try:
9
+ lines = (PROC_ROOT / str(pid) / "cgroup").read_text().splitlines()
10
+ except OSError:
11
+ return None
12
+ for line in lines:
13
+ if line.startswith("0::"):
14
+ return CGROUP_ROOT / line.removeprefix("0::").lstrip("/")
15
+ return None
@@ -0,0 +1,100 @@
1
+ import asyncio
2
+ import shutil
3
+ import time
4
+ from pathlib import Path
5
+
6
+ from labnotes.runner.cgroup_memory import cgroup_memory
7
+ from labnotes.runner.container_cgroup import container_cgroup
8
+ from labnotes.runner.docker_output import docker_output
9
+
10
+ LABEL = "labnotes.run"
11
+
12
+
13
+ class ContainerMemory:
14
+ """Track this run's Docker cgroups without starting a CLI process per sample."""
15
+
16
+ def __init__(self, run_id: str) -> None:
17
+ self.run_id = run_id
18
+ self.cgroups: dict[str, Path] = {}
19
+ self.events: asyncio.subprocess.Process | None = None
20
+ self.watcher: asyncio.Task[None] | None = None
21
+
22
+ async def start(self) -> None:
23
+ if shutil.which("docker") is None:
24
+ return
25
+ since = str(int(time.time()))
26
+ ids = await asyncio.to_thread(
27
+ docker_output,
28
+ "ps",
29
+ "--quiet",
30
+ "--no-trunc",
31
+ "--filter",
32
+ f"label={LABEL}={self.run_id}",
33
+ )
34
+ if ids is None:
35
+ return
36
+ for container_id in ids.splitlines():
37
+ await self._add(container_id)
38
+ try:
39
+ self.events = await asyncio.create_subprocess_exec(
40
+ "docker",
41
+ "events",
42
+ "--since",
43
+ since,
44
+ "--filter",
45
+ "type=container",
46
+ "--filter",
47
+ f"label={LABEL}={self.run_id}",
48
+ "--format",
49
+ "{{.Actor.ID}} {{.Action}}",
50
+ stdout=asyncio.subprocess.PIPE,
51
+ stderr=asyncio.subprocess.DEVNULL,
52
+ )
53
+ except OSError:
54
+ return
55
+ self.watcher = asyncio.create_task(self._watch())
56
+
57
+ async def _add(self, container_id: str) -> None:
58
+ raw_pid = await asyncio.to_thread(
59
+ docker_output, "inspect", "--format", "{{.State.Pid}}", container_id
60
+ )
61
+ if raw_pid is None:
62
+ return
63
+ try:
64
+ pid = int(raw_pid.strip())
65
+ except ValueError:
66
+ return
67
+ if pid > 0 and (path := container_cgroup(pid)) is not None:
68
+ self.cgroups[container_id] = path
69
+
70
+ async def _watch(self) -> None:
71
+ assert self.events is not None
72
+ assert self.events.stdout is not None
73
+ while line := await self.events.stdout.readline():
74
+ container_id, _, action = line.decode().strip().partition(" ")
75
+ if action == "start":
76
+ await self._add(container_id)
77
+ elif action in {"die", "destroy", "stop"}:
78
+ self.cgroups.pop(container_id, None)
79
+
80
+ def usage(self) -> int:
81
+ total = 0
82
+ for path in self.cgroups.values():
83
+ try:
84
+ total += cgroup_memory(path)
85
+ except (OSError, ValueError):
86
+ pass
87
+ return total
88
+
89
+ async def close(self) -> None:
90
+ if self.watcher is not None:
91
+ self.watcher.cancel()
92
+ await asyncio.gather(self.watcher, return_exceptions=True)
93
+ if self.events is not None:
94
+ if self.events.returncode is None:
95
+ self.events.terminate()
96
+ try:
97
+ await asyncio.wait_for(self.events.wait(), timeout=1)
98
+ except TimeoutError:
99
+ self.events.kill()
100
+ await self.events.wait()
@@ -0,0 +1,11 @@
1
+ import subprocess
2
+
3
+
4
+ def docker_output(*args: str) -> str | None:
5
+ try:
6
+ result = subprocess.run(
7
+ ["docker", *args], capture_output=True, text=True, timeout=3, check=False
8
+ )
9
+ except (OSError, subprocess.TimeoutExpired):
10
+ return None
11
+ return result.stdout if result.returncode == 0 else None
@@ -7,7 +7,7 @@ from pathlib import Path
7
7
 
8
8
  import psutil
9
9
 
10
- from labnotes.log_path import LOG_ENV
10
+ from labnotes.log_path import LOG_ENV, RUN_ENV
11
11
  from labnotes.runner.exit_signal import exit_signal
12
12
  from labnotes.runner.record_memory import record_memory
13
13
  from labnotes.runner.shell_command import shell_command
@@ -66,6 +66,7 @@ async def execute_run(
66
66
  "CLICOLOR_FORCE": "1",
67
67
  **os.environ,
68
68
  LOG_ENV: str(folder / "log.jsonl"),
69
+ RUN_ENV: str(folder.resolve()),
69
70
  },
70
71
  stdout=stdout,
71
72
  stderr=stderr,
@@ -0,0 +1,28 @@
1
+ import asyncio
2
+ import json
3
+ import time
4
+ from pathlib import Path
5
+
6
+ from labnotes.runner.container_memory import ContainerMemory
7
+ from labnotes.utils.tree_usage import tree_usage
8
+
9
+
10
+ async def record_memory(path: Path, pid: int, created: float, interval: float) -> None:
11
+ containers = ContainerMemory(str(path.parent.resolve()))
12
+ try:
13
+ await containers.start()
14
+ with path.open("a", encoding="utf-8") as samples:
15
+ while True:
16
+ usage = tree_usage(pid, created)
17
+ if usage is not None:
18
+ rss, processes = usage
19
+ sample = {
20
+ "time": time.time(),
21
+ "rss": rss + containers.usage(),
22
+ "processes": processes,
23
+ }
24
+ samples.write(json.dumps(sample) + "\n")
25
+ samples.flush()
26
+ await asyncio.sleep(interval)
27
+ finally:
28
+ await containers.close()
@@ -1,18 +0,0 @@
1
- import asyncio
2
- import json
3
- import time
4
- from pathlib import Path
5
-
6
- from labnotes.utils.tree_usage import tree_usage
7
-
8
-
9
- async def record_memory(path: Path, pid: int, created: float, interval: float) -> None:
10
- with path.open("a", encoding="utf-8") as samples:
11
- while True:
12
- usage = tree_usage(pid, created)
13
- if usage is not None:
14
- rss, processes = usage
15
- sample = {"time": time.time(), "rss": rss, "processes": processes}
16
- samples.write(json.dumps(sample) + "\n")
17
- samples.flush()
18
- await asyncio.sleep(interval)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes