labnotes 0.2.5__tar.gz → 0.2.6__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 (215) hide show
  1. {labnotes-0.2.5 → labnotes-0.2.6}/PKG-INFO +6 -2
  2. {labnotes-0.2.5 → labnotes-0.2.6}/README.md +4 -1
  3. {labnotes-0.2.5 → labnotes-0.2.6}/pyproject.toml +1 -0
  4. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/context.md +4 -1
  5. labnotes-0.2.6/src/labnotes/copy_experiment.py +12 -0
  6. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/create_experiment.py +26 -3
  7. labnotes-0.2.6/src/labnotes/experiment_definition.py +22 -0
  8. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/experiments.py +45 -2
  9. labnotes-0.2.6/src/labnotes/parse_param_value.py +8 -0
  10. labnotes-0.2.6/src/labnotes/parse_params.py +11 -0
  11. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/read_experiment_config.py +4 -0
  12. {labnotes-0.2.5 → labnotes-0.2.6}/uv.lock +11 -0
  13. {labnotes-0.2.5 → labnotes-0.2.6}/.gitignore +0 -0
  14. {labnotes-0.2.5 → labnotes-0.2.6}/CHANGELOG.md +0 -0
  15. {labnotes-0.2.5 → labnotes-0.2.6}/MIGRATIONS.md +0 -0
  16. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/__init__.py +0 -0
  17. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/__main__.py +0 -0
  18. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/asset_url.py +0 -0
  19. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/check_counts.py +0 -0
  20. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/cli.py +0 -0
  21. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/compose_experiment.py +0 -0
  22. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/context_guide.py +0 -0
  23. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/count_groups.py +0 -0
  24. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/count_runs.py +0 -0
  25. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/create_app.py +0 -0
  26. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/create_note.py +0 -0
  27. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/data_html.py +0 -0
  28. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/default_config_text.py +0 -0
  29. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/default_counts.py +0 -0
  30. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/drain_watch.py +0 -0
  31. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/experiment_notes.py +0 -0
  32. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/experiment_runs.py +0 -0
  33. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/file_changes.py +0 -0
  34. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/format_duration.py +0 -0
  35. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/format_started.py +0 -0
  36. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/format_value.py +0 -0
  37. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/get_state.py +0 -0
  38. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/grid_cell.py +0 -0
  39. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/group_runs.py +0 -0
  40. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/init.py +0 -0
  41. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/invocation_started.py +0 -0
  42. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/is_non_negative_int.py +0 -0
  43. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/is_non_negative_number.py +0 -0
  44. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/is_positive_int.py +0 -0
  45. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/latest_log.py +0 -0
  46. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/list_experiments.py +0 -0
  47. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/list_runs.py +0 -0
  48. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/load_config.py +0 -0
  49. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/load_experiment.py +0 -0
  50. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/load_run.py +0 -0
  51. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/log.py +0 -0
  52. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/log_path.py +0 -0
  53. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/memory_interval.py +0 -0
  54. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/models.py +0 -0
  55. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/modes.py +0 -0
  56. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/nests_runs.py +0 -0
  57. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/note_path.py +0 -0
  58. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/notebook_kernel.py +0 -0
  59. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/output_html.py +0 -0
  60. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/parse_run_view.py +0 -0
  61. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/parse_subset.py +0 -0
  62. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/pivot_runs.py +0 -0
  63. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/print_rows.py +0 -0
  64. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/python_blocks.py +0 -0
  65. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/read_config.py +0 -0
  66. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/read_path.py +0 -0
  67. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/render_markdown.py +0 -0
  68. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/retries.py +0 -0
  69. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/run_output.py +0 -0
  70. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/__init__.py +0 -0
  71. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/cgroup_memory.py +0 -0
  72. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/check_args.py +0 -0
  73. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/check_hook_placeholders.py +0 -0
  74. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/check_subset.py +0 -0
  75. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/claim_run.py +0 -0
  76. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/command_flags.py +0 -0
  77. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/container_cgroup.py +0 -0
  78. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/container_memory.py +0 -0
  79. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/continue_run.py +0 -0
  80. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/describe_event.py +0 -0
  81. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/docker_output.py +0 -0
  82. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/execute_run.py +0 -0
  83. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/exit_signal.py +0 -0
  84. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/fill_dirs.py +0 -0
  85. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/fill_experiment_env.py +0 -0
  86. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/finalize_lost_runs.py +0 -0
  87. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/first_invocation.py +0 -0
  88. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/flag_name.py +0 -0
  89. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/group_args.py +0 -0
  90. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/in_subset.py +0 -0
  91. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/invocation_hook.py +0 -0
  92. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/invocation_meta.py +0 -0
  93. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/new_param_run_id.py +0 -0
  94. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/new_run_id.py +0 -0
  95. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/new_stamped_run_id.py +0 -0
  96. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/override_flags.py +0 -0
  97. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/param_args.py +0 -0
  98. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/param_grid.py +0 -0
  99. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/param_run_name.py +0 -0
  100. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/placeholders.py +0 -0
  101. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/plan_runs.py +0 -0
  102. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/prepare_experiment.py +0 -0
  103. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/project_snapshot.py +0 -0
  104. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/record_memory.py +0 -0
  105. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/run_experiment.py +0 -0
  106. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/run_folder.py +0 -0
  107. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/run_git.py +0 -0
  108. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/run_headless.py +0 -0
  109. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/run_locked.py +0 -0
  110. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/runs_parent.py +0 -0
  111. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/shell_command.py +0 -0
  112. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/signal_tree.py +0 -0
  113. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/start_experiment.py +0 -0
  114. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/stop_then_kill.py +0 -0
  115. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/untracked_diff.py +0 -0
  116. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/wait_for_tree.py +0 -0
  117. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runner/write_run_json.py +0 -0
  118. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/runs_view_command.py +0 -0
  119. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/save_conflict_copy.py +0 -0
  120. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/save_note.py +0 -0
  121. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/saved_outputs.py +0 -0
  122. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/serve_app.py +0 -0
  123. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/server.py +0 -0
  124. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/set_state.py +0 -0
  125. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/shared_params.py +0 -0
  126. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/sort_href.py +0 -0
  127. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/sql_quote.py +0 -0
  128. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/state_path.py +0 -0
  129. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/elapsed.js +0 -0
  130. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/experiment.js +0 -0
  131. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/labnotes.css +0 -0
  132. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/local-time.js +0 -0
  133. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/new-note.js +0 -0
  134. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/note-edit.js +0 -0
  135. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/note-editor.js +0 -0
  136. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/row-link.js +0 -0
  137. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/run-cell.js +0 -0
  138. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/run-output.js +0 -0
  139. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/static/view-toggle.js +0 -0
  140. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/summarize_config.py +0 -0
  141. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/__init__.py +0 -0
  142. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/experiment_rows.py +0 -0
  143. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/experiment_run_ids.py +0 -0
  144. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/experiment_toml.py +0 -0
  145. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/experiments_ddl.py +0 -0
  146. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/file_prefix.py +0 -0
  147. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/file_text.py +0 -0
  148. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/iso_time.py +0 -0
  149. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/list_experiment_runs.py +0 -0
  150. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/list_resumable_runs.py +0 -0
  151. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/note_text.py +0 -0
  152. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/open_tables.py +0 -0
  153. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/path_glob.py +0 -0
  154. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/path_pattern.py +0 -0
  155. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/read_blocks.py +0 -0
  156. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/read_experiment.py +0 -0
  157. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/read_file.py +0 -0
  158. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/read_log.py +0 -0
  159. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/read_memory.py +0 -0
  160. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/read_run.py +0 -0
  161. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/read_run_file.py +0 -0
  162. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/runs_columns.py +0 -0
  163. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tables/summarize_experiments.py +0 -0
  164. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/templates/base.html +0 -0
  165. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/templates/editor_imports.html +0 -0
  166. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/templates/experiment.html +0 -0
  167. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/templates/index.html +0 -0
  168. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/templates/note.html +0 -0
  169. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/templates/run.html +0 -0
  170. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/__init__.py +0 -0
  171. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/browse_app.py +0 -0
  172. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/browse_tui.py +0 -0
  173. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/check_view_response.py +0 -0
  174. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/config.py +0 -0
  175. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/dim.py +0 -0
  176. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/experiment_screen.py +0 -0
  177. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/experiments_screen.py +0 -0
  178. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/fill_table.py +0 -0
  179. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/format_cell.py +0 -0
  180. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/format_elapsed.py +0 -0
  181. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/group_cells.py +0 -0
  182. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/invocation_columns.py +0 -0
  183. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/invocation_rows.py +0 -0
  184. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/leave_prompt.py +0 -0
  185. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/log_block.py +0 -0
  186. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/log_blocks.py +0 -0
  187. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/native_rows.py +0 -0
  188. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/reload_on_change.py +0 -0
  189. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/run_app.py +0 -0
  190. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/run_cells.py +0 -0
  191. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/run_output.py +0 -0
  192. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/run_screen.py +0 -0
  193. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/run_tui.py +0 -0
  194. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/runs_table.py +0 -0
  195. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/runs_view.py +0 -0
  196. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/stopped_title.py +0 -0
  197. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/view_column.py +0 -0
  198. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/tui/view_process.py +0 -0
  199. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/utils/__init__.py +0 -0
  200. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/utils/format_bytes.py +0 -0
  201. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/utils/process_alive.py +0 -0
  202. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/utils/run_usage.py +0 -0
  203. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/utils/tree_rss.py +0 -0
  204. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/utils/tree_usage.py +0 -0
  205. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/view/__init__.py +0 -0
  206. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/view/answer.py +0 -0
  207. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/view/build.py +0 -0
  208. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/view/respond.py +0 -0
  209. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/view/serve.py +0 -0
  210. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/wait_for_note.py +0 -0
  211. {labnotes-0.2.5 → labnotes-0.2.6}/src/labnotes/watch_rows.py +0 -0
  212. {labnotes-0.2.5 → labnotes-0.2.6}/testing-conventions.toml +0 -0
  213. {labnotes-0.2.5 → labnotes-0.2.6}/tests/conftest.py +0 -0
  214. {labnotes-0.2.5 → labnotes-0.2.6}/tests/e2e/conftest.py +0 -0
  215. {labnotes-0.2.5 → labnotes-0.2.6}/tests/integration/conftest.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.2.5
3
+ Version: 0.2.6
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>
@@ -20,6 +20,7 @@ Requires-Dist: psutil>=7.2.2
20
20
  Requires-Dist: rich>=14
21
21
  Requires-Dist: starlette>=1.8.0
22
22
  Requires-Dist: textual>=8.2.8
23
+ Requires-Dist: tomlkit>=0.15.2
23
24
  Requires-Dist: uvicorn>=0.54.0
24
25
  Description-Content-Type: text/markdown
25
26
 
@@ -83,7 +84,10 @@ one. Piped, they print a table.
83
84
 
84
85
  `labnotes experiments new [NAME]` creates an experiment folder with a starter
85
86
  `experiment.toml`, picking a random name if you give none. `new` itself can't
86
- be an experiment name.
87
+ be an experiment name. `--command`, `--param NAME=VALUE` (repeat it for several
88
+ values) and `--repeats` write those settings into the file. `--from OTHER`
89
+ copies another experiment's `experiment.toml` and files, never its runs, and
90
+ records `from = "OTHER"`; the other flags replace the copied values.
87
91
 
88
92
  `labnotes experiments foo run` runs `foo` from its `experiment.toml`, in the
89
93
  experiments folder:
@@ -58,7 +58,10 @@ one. Piped, they print a table.
58
58
 
59
59
  `labnotes experiments new [NAME]` creates an experiment folder with a starter
60
60
  `experiment.toml`, picking a random name if you give none. `new` itself can't
61
- be an experiment name.
61
+ be an experiment name. `--command`, `--param NAME=VALUE` (repeat it for several
62
+ values) and `--repeats` write those settings into the file. `--from OTHER`
63
+ copies another experiment's `experiment.toml` and files, never its runs, and
64
+ records `from = "OTHER"`; the other flags replace the copied values.
62
65
 
63
66
  `labnotes experiments foo run` runs `foo` from its `experiment.toml`, in the
64
67
  experiments folder:
@@ -25,6 +25,7 @@ dependencies = [
25
25
  "rich>=14",
26
26
  "starlette>=1.8.0",
27
27
  "textual>=8.2.8",
28
+ "tomlkit>=0.15.2",
28
29
  "uvicorn>=0.54.0",
29
30
  ]
30
31
 
@@ -43,7 +43,10 @@ keys that never come.
43
43
 
44
44
  `labnotes experiments new [NAME]` creates an experiment folder with a starter
45
45
  `experiment.toml`, picking a random name if you give none. `new` itself can't
46
- be an experiment name.
46
+ be an experiment name. `--command`, `--param NAME=VALUE` (repeat it for several
47
+ values) and `--repeats` write those settings into the file. `--from OTHER`
48
+ copies another experiment's `experiment.toml` and files, never its runs, and
49
+ records `from = "OTHER"`; the other flags replace the copied values.
47
50
 
48
51
  `labnotes experiments foo run` runs `foo` from its `experiment.toml`, in the
49
52
  experiments folder:
@@ -0,0 +1,12 @@
1
+ import shutil
2
+ from pathlib import Path
3
+
4
+
5
+ def copy_experiment(source: Path, target: Path, runs: Path) -> None:
6
+ def skip(folder: str, names: list[str]) -> set[str]:
7
+ here = Path(folder)
8
+ if here == runs:
9
+ return {name for name in names if (here / name / "run.json").exists()}
10
+ return {name for name in names if here / name == runs}
11
+
12
+ shutil.copytree(source, target, ignore=skip, symlinks=True)
@@ -1,7 +1,10 @@
1
1
  from pathlib import Path
2
2
 
3
+ from labnotes.copy_experiment import copy_experiment
4
+ from labnotes.experiment_definition import experiment_definition
3
5
  from labnotes.models import LabnotesConfig
4
6
  from labnotes.runner.new_run_id import new_run_id
7
+ from labnotes.runner.runs_parent import runs_parent
5
8
 
6
9
  STARTER = """\
7
10
  # Runs from the project root. {run_dir} becomes the run's folder.
@@ -14,7 +17,14 @@ repeats = 1
14
17
  """
15
18
 
16
19
 
17
- def create_experiment(config: LabnotesConfig, name: str | None) -> Path:
20
+ def create_experiment(
21
+ config: LabnotesConfig,
22
+ name: str | None,
23
+ source: str | None = None,
24
+ command: str | None = None,
25
+ repeats: int | None = None,
26
+ params: dict[str, list[object]] | None = None,
27
+ ) -> Path:
18
28
  def folder(candidate: str) -> Path:
19
29
  return config.root / config.experiments.replace("{experiment}", candidate)
20
30
 
@@ -31,6 +41,19 @@ def create_experiment(config: LabnotesConfig, name: str | None) -> Path:
31
41
  path = folder(name)
32
42
  if path.exists():
33
43
  raise ValueError(f"{name} already exists at {path}.")
34
- path.mkdir(parents=True)
35
- (path / "experiment.toml").write_text(STARTER, encoding="utf-8")
44
+ text = STARTER
45
+ if source is not None:
46
+ origin = folder(source) / "experiment.toml"
47
+ if not origin.is_file():
48
+ raise ValueError(f"No experiment.toml for {source} at {origin}.")
49
+ text = origin.read_text(encoding="utf-8")
50
+ copy_experiment(
51
+ origin.parent, path, runs_parent(config.root, config.runs, source)
52
+ )
53
+ else:
54
+ path.mkdir(parents=True)
55
+ (path / "experiment.toml").write_text(
56
+ experiment_definition(text, source, command, repeats, params or {}),
57
+ encoding="utf-8",
58
+ )
36
59
  return path
@@ -0,0 +1,22 @@
1
+ import tomlkit
2
+
3
+
4
+ def experiment_definition(
5
+ text: str,
6
+ source: str | None,
7
+ command: str | None,
8
+ repeats: int | None,
9
+ params: dict[str, list[object]],
10
+ ) -> str:
11
+ document = tomlkit.parse(text)
12
+ document.pop("from", None)
13
+ if command is not None:
14
+ document["command"] = command
15
+ if repeats is not None:
16
+ document["repeats"] = repeats
17
+ if params:
18
+ table = document.setdefault("params", tomlkit.table())
19
+ for name, values in params.items():
20
+ table[name] = values
21
+ head = "" if source is None else tomlkit.dumps({"from": source})
22
+ return head + tomlkit.dumps(document)
@@ -8,6 +8,7 @@ from labnotes.create_experiment import create_experiment
8
8
  from labnotes.runner.start_experiment import start_experiment
9
9
  from labnotes.utils.format_bytes import format_bytes
10
10
  from labnotes.load_config import load_config
11
+ from labnotes.parse_params import parse_params
11
12
  from labnotes.parse_subset import parse_subset
12
13
  from labnotes.print_rows import print_rows
13
14
  from labnotes.read_config import CONFIG_FILE
@@ -32,7 +33,8 @@ class PassthroughCommand(click.Command):
32
33
  @click.command(
33
34
  cls=PassthroughCommand,
34
35
  help="List experiments, show one, list its runs, or run it."
35
- " `new [NAME]` creates one. With run, --PARAM=VALUE runs only the"
36
+ " `new [NAME]` creates one, from --command, --param, --repeats and"
37
+ " --from OTHER if given. With run, --PARAM=VALUE runs only the"
36
38
  " matrix cells where that param has that value (repeat it for several"
37
39
  " values), and anything after --"
38
40
  " is appended to every run's command and recorded in run.json, so never"
@@ -79,6 +81,31 @@ class PassthroughCommand(click.Command):
79
81
  help="With run: shell command to run once after every run ends."
80
82
  " Overrides experiment.toml and .labnotes.toml.",
81
83
  )
84
+ @click.option(
85
+ "--command",
86
+ metavar="COMMAND",
87
+ help="With new: the experiment's command.",
88
+ )
89
+ @click.option(
90
+ "--param",
91
+ "params",
92
+ multiple=True,
93
+ metavar="NAME=VALUE",
94
+ help="With new: a value for a param, parsed as TOML or else taken as a"
95
+ " string. Repeat it for several values.",
96
+ )
97
+ @click.option(
98
+ "--repeats",
99
+ type=click.IntRange(min=1),
100
+ help="With new: how many times each combination runs.",
101
+ )
102
+ @click.option(
103
+ "--from",
104
+ "source",
105
+ metavar="OTHER",
106
+ help="With new: start from OTHER's experiment.toml and files, never its"
107
+ " runs. Other flags replace the copied values.",
108
+ )
82
109
  @click.pass_context
83
110
  def experiments(
84
111
  ctx: click.Context,
@@ -90,6 +117,10 @@ def experiments(
90
117
  timeout: int | None,
91
118
  setup: str | None,
92
119
  teardown: str | None,
120
+ command: str | None,
121
+ params: tuple[str, ...],
122
+ repeats: int | None,
123
+ source: str | None,
93
124
  ) -> None:
94
125
  passthrough: list[str] | None = ctx.meta.get(PASSTHROUGH)
95
126
  config = load_config(ctx)
@@ -97,10 +128,22 @@ def experiments(
97
128
  raise click.ClickException(
98
129
  f"No {CONFIG_FILE} here. Run labnotes init, or pass -c PATH."
99
130
  )
131
+ if name != "new":
132
+ given = {
133
+ "--command": command,
134
+ "--param": params or None,
135
+ "--repeats": repeats,
136
+ "--from": source,
137
+ }
138
+ for flag, value in given.items():
139
+ if value is not None:
140
+ raise click.UsageError(f"{flag} needs new.")
100
141
  match name, action:
101
142
  case "new", _:
102
143
  try:
103
- path = create_experiment(config, action)
144
+ path = create_experiment(
145
+ config, action, source, command, repeats, parse_params(list(params))
146
+ )
104
147
  except ValueError as error:
105
148
  raise click.ClickException(str(error)) from error
106
149
  click.echo(f"Created {path / 'experiment.toml'}")
@@ -0,0 +1,8 @@
1
+ import tomllib
2
+
3
+
4
+ def parse_param_value(value: str) -> object:
5
+ try:
6
+ return tomllib.loads(f"value = {value}")["value"]
7
+ except tomllib.TOMLDecodeError:
8
+ return value
@@ -0,0 +1,11 @@
1
+ from labnotes.parse_param_value import parse_param_value
2
+
3
+
4
+ def parse_params(pairs: list[str]) -> dict[str, list[object]]:
5
+ params: dict[str, list[object]] = {}
6
+ for pair in pairs:
7
+ name, equals, value = pair.partition("=")
8
+ if not equals or not name:
9
+ raise ValueError(f"--param {pair} must look like NAME=VALUE.")
10
+ params.setdefault(name, []).append(parse_param_value(value))
11
+ return params
@@ -28,6 +28,7 @@ SETTINGS = {
28
28
  "teardown",
29
29
  "env",
30
30
  "continues",
31
+ "from",
31
32
  }
32
33
  HOOKS = ("setup", "teardown")
33
34
  VIEWS = {"runs"}
@@ -86,6 +87,9 @@ def read_experiment_config(
86
87
  memory_interval = values.get("memory_interval", DEFAULT_MEMORY_INTERVAL)
87
88
  if not is_non_negative_number(memory_interval):
88
89
  raise ValueError(f"{path}: memory_interval must be 0 or more seconds")
90
+ origin = values.get("from")
91
+ if origin is not None and (not isinstance(origin, str) or not origin.strip()):
92
+ raise ValueError(f"{path}: from must be an experiment name")
89
93
  for hook in HOOKS:
90
94
  run = values.get(hook)
91
95
  if run is not None and (not isinstance(run, str) or not run.strip()):
@@ -568,6 +568,7 @@ dependencies = [
568
568
  { name = "rich" },
569
569
  { name = "starlette" },
570
570
  { name = "textual" },
571
+ { name = "tomlkit" },
571
572
  { name = "uvicorn" },
572
573
  ]
573
574
 
@@ -597,6 +598,7 @@ requires-dist = [
597
598
  { name = "rich", specifier = ">=14" },
598
599
  { name = "starlette", specifier = ">=1.8.0" },
599
600
  { name = "textual", specifier = ">=8.2.8" },
601
+ { name = "tomlkit", specifier = ">=0.15.2" },
600
602
  { name = "uvicorn", specifier = ">=0.54.0" },
601
603
  ]
602
604
 
@@ -1307,6 +1309,15 @@ wheels = [
1307
1309
  { url = "https://files.pythonhosted.org/packages/fb/be/35261223d9416a0751cdff1c7b4a6f881387218a12d439fe22fefebc8c04/textual-8.2.8-py3-none-any.whl", hash = "sha256:267375fd402dc8d981457212efa71f0e3365fd17bba144ba9bb3ed7563cb374a", size = 731418, upload-time = "2026-06-30T06:51:26.364Z" },
1308
1310
  ]
1309
1311
 
1312
+ [[package]]
1313
+ name = "tomlkit"
1314
+ version = "0.15.2"
1315
+ source = { registry = "https://pypi.org/simple" }
1316
+ sdist = { url = "https://files.pythonhosted.org/packages/08/72/fc01563df95c189a9f65850096bdc205bc2fe37da6c740cd64afaf18d07e/tomlkit-0.15.2.tar.gz", hash = "sha256:83e32755ba1d20d9340bd069ee97e9f6232ba2346b63ffcba425de01f7e327a6", size = 190522, upload-time = "2026-10-16T04:07:08.166Z" }
1317
+ wheels = [
1318
+ { url = "https://files.pythonhosted.org/packages/da/46/7d84138a60a8bd796825b2d38ebe92d0d16501ca57d501b7c5d677401bbc/tomlkit-0.15.2-py3-none-any.whl", hash = "sha256:05562412163a3037b94497385fb7f9204c637bc605af7a48199e41cf2272f437", size = 52225, upload-time = "2026-10-16T04:07:09.216Z" },
1319
+ ]
1320
+
1310
1321
  [[package]]
1311
1322
  name = "tornado"
1312
1323
  version = "6.5.10"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes