labnotes 0.1.2__tar.gz → 0.2.0__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 (203) hide show
  1. {labnotes-0.1.2 → labnotes-0.2.0}/PKG-INFO +1 -1
  2. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/context.md +7 -9
  3. labnotes-0.2.0/src/labnotes/runner/check_hook_placeholders.py +12 -0
  4. labnotes-0.2.0/src/labnotes/runner/fill_experiment_env.py +23 -0
  5. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/placeholders.py +0 -1
  6. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/run_experiment.py +15 -15
  7. labnotes-0.1.2/src/labnotes/runner/fill_experiment_env.py +0 -34
  8. labnotes-0.1.2/src/labnotes/runner/hook_dirs.py +0 -21
  9. {labnotes-0.1.2 → labnotes-0.2.0}/.gitignore +0 -0
  10. {labnotes-0.1.2 → labnotes-0.2.0}/CHANGELOG.md +0 -0
  11. {labnotes-0.1.2 → labnotes-0.2.0}/MIGRATIONS.md +0 -0
  12. {labnotes-0.1.2 → labnotes-0.2.0}/README.md +0 -0
  13. {labnotes-0.1.2 → labnotes-0.2.0}/pyproject.toml +0 -0
  14. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/__init__.py +0 -0
  15. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/__main__.py +0 -0
  16. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/asset_url.py +0 -0
  17. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/check_counts.py +0 -0
  18. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/cli.py +0 -0
  19. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/compose_experiment.py +0 -0
  20. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/context_guide.py +0 -0
  21. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/count_groups.py +0 -0
  22. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/count_runs.py +0 -0
  23. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/create_app.py +0 -0
  24. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/create_experiment.py +0 -0
  25. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/create_note.py +0 -0
  26. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/data_html.py +0 -0
  27. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/default_config_text.py +0 -0
  28. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/default_counts.py +0 -0
  29. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/drain_watch.py +0 -0
  30. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/experiment_notes.py +0 -0
  31. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/experiment_runs.py +0 -0
  32. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/experiments.py +0 -0
  33. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/file_changes.py +0 -0
  34. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/format_duration.py +0 -0
  35. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/format_started.py +0 -0
  36. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/format_value.py +0 -0
  37. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/get_state.py +0 -0
  38. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/grid_cell.py +0 -0
  39. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/group_runs.py +0 -0
  40. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/init.py +0 -0
  41. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/invocation_started.py +0 -0
  42. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/is_non_negative_int.py +0 -0
  43. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/is_non_negative_number.py +0 -0
  44. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/is_positive_int.py +0 -0
  45. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/list_experiments.py +0 -0
  46. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/list_runs.py +0 -0
  47. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/load_config.py +0 -0
  48. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/load_experiment.py +0 -0
  49. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/load_run.py +0 -0
  50. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/log.py +0 -0
  51. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/log_path.py +0 -0
  52. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/memory_interval.py +0 -0
  53. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/models.py +0 -0
  54. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/modes.py +0 -0
  55. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/nests_runs.py +0 -0
  56. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/note_path.py +0 -0
  57. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/notebook_kernel.py +0 -0
  58. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/output_html.py +0 -0
  59. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/parse_run_view.py +0 -0
  60. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/parse_subset.py +0 -0
  61. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/pivot_runs.py +0 -0
  62. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/print_rows.py +0 -0
  63. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/python_blocks.py +0 -0
  64. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/read_config.py +0 -0
  65. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/read_path.py +0 -0
  66. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/render_markdown.py +0 -0
  67. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/retries.py +0 -0
  68. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/run_output.py +0 -0
  69. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/__init__.py +0 -0
  70. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/cgroup_memory.py +0 -0
  71. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/check_args.py +0 -0
  72. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/check_subset.py +0 -0
  73. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/container_cgroup.py +0 -0
  74. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/container_memory.py +0 -0
  75. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/describe_event.py +0 -0
  76. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/docker_output.py +0 -0
  77. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/execute_run.py +0 -0
  78. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/exit_signal.py +0 -0
  79. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/fill_dirs.py +0 -0
  80. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/finalize_lost_runs.py +0 -0
  81. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/flag_name.py +0 -0
  82. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/in_subset.py +0 -0
  83. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/invocation_hook.py +0 -0
  84. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/invocation_meta.py +0 -0
  85. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/new_param_run_id.py +0 -0
  86. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/new_run_id.py +0 -0
  87. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/new_stamped_run_id.py +0 -0
  88. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/param_args.py +0 -0
  89. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/param_grid.py +0 -0
  90. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/param_run_name.py +0 -0
  91. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/plan_runs.py +0 -0
  92. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/prepare_experiment.py +0 -0
  93. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/project_snapshot.py +0 -0
  94. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/read_experiment_config.py +0 -0
  95. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/record_memory.py +0 -0
  96. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/run_folder.py +0 -0
  97. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/run_git.py +0 -0
  98. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/run_headless.py +0 -0
  99. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/runs_parent.py +0 -0
  100. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/shell_command.py +0 -0
  101. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/signal_tree.py +0 -0
  102. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/start_experiment.py +0 -0
  103. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/stop_then_kill.py +0 -0
  104. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/wait_for_tree.py +0 -0
  105. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runner/write_run_json.py +0 -0
  106. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/runs_view_command.py +0 -0
  107. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/save_conflict_copy.py +0 -0
  108. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/save_note.py +0 -0
  109. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/saved_outputs.py +0 -0
  110. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/serve_app.py +0 -0
  111. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/server.py +0 -0
  112. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/set_state.py +0 -0
  113. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/shared_params.py +0 -0
  114. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/sort_href.py +0 -0
  115. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/sql_quote.py +0 -0
  116. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/state_path.py +0 -0
  117. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/elapsed.js +0 -0
  118. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/experiment.js +0 -0
  119. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/labnotes.css +0 -0
  120. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/local-time.js +0 -0
  121. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/new-note.js +0 -0
  122. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/note-edit.js +0 -0
  123. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/note-editor.js +0 -0
  124. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/row-link.js +0 -0
  125. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/run-cell.js +0 -0
  126. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/run-output.js +0 -0
  127. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/static/view-toggle.js +0 -0
  128. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/summarize_config.py +0 -0
  129. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/__init__.py +0 -0
  130. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/experiment_rows.py +0 -0
  131. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/experiment_run_ids.py +0 -0
  132. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/experiment_toml.py +0 -0
  133. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/experiments_ddl.py +0 -0
  134. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/file_prefix.py +0 -0
  135. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/file_text.py +0 -0
  136. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/iso_time.py +0 -0
  137. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/list_experiment_runs.py +0 -0
  138. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/note_text.py +0 -0
  139. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/open_tables.py +0 -0
  140. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/path_glob.py +0 -0
  141. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/path_pattern.py +0 -0
  142. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/read_blocks.py +0 -0
  143. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/read_experiment.py +0 -0
  144. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/read_file.py +0 -0
  145. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/read_log.py +0 -0
  146. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/read_memory.py +0 -0
  147. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/read_run.py +0 -0
  148. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/read_run_file.py +0 -0
  149. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/runs_columns.py +0 -0
  150. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tables/summarize_experiments.py +0 -0
  151. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/templates/base.html +0 -0
  152. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/templates/editor_imports.html +0 -0
  153. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/templates/experiment.html +0 -0
  154. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/templates/index.html +0 -0
  155. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/templates/note.html +0 -0
  156. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/templates/run.html +0 -0
  157. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/__init__.py +0 -0
  158. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/browse_app.py +0 -0
  159. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/browse_tui.py +0 -0
  160. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/check_view_response.py +0 -0
  161. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/config.py +0 -0
  162. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/dim.py +0 -0
  163. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/experiment_screen.py +0 -0
  164. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/experiments_screen.py +0 -0
  165. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/fill_table.py +0 -0
  166. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/format_cell.py +0 -0
  167. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/format_elapsed.py +0 -0
  168. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/group_cells.py +0 -0
  169. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/invocation_columns.py +0 -0
  170. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/invocation_rows.py +0 -0
  171. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/leave_prompt.py +0 -0
  172. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/log_block.py +0 -0
  173. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/log_blocks.py +0 -0
  174. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/native_rows.py +0 -0
  175. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/reload_on_change.py +0 -0
  176. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/run_app.py +0 -0
  177. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/run_cells.py +0 -0
  178. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/run_output.py +0 -0
  179. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/run_screen.py +0 -0
  180. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/run_tui.py +0 -0
  181. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/runs_table.py +0 -0
  182. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/runs_view.py +0 -0
  183. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/stopped_title.py +0 -0
  184. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/view_column.py +0 -0
  185. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/tui/view_process.py +0 -0
  186. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/utils/__init__.py +0 -0
  187. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/utils/format_bytes.py +0 -0
  188. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/utils/process_alive.py +0 -0
  189. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/utils/run_usage.py +0 -0
  190. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/utils/tree_rss.py +0 -0
  191. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/utils/tree_usage.py +0 -0
  192. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/view/__init__.py +0 -0
  193. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/view/answer.py +0 -0
  194. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/view/build.py +0 -0
  195. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/view/respond.py +0 -0
  196. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/view/serve.py +0 -0
  197. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/wait_for_note.py +0 -0
  198. {labnotes-0.1.2 → labnotes-0.2.0}/src/labnotes/watch_rows.py +0 -0
  199. {labnotes-0.1.2 → labnotes-0.2.0}/testing-conventions.toml +0 -0
  200. {labnotes-0.1.2 → labnotes-0.2.0}/tests/conftest.py +0 -0
  201. {labnotes-0.1.2 → labnotes-0.2.0}/tests/e2e/conftest.py +0 -0
  202. {labnotes-0.1.2 → labnotes-0.2.0}/tests/integration/conftest.py +0 -0
  203. {labnotes-0.1.2 → labnotes-0.2.0}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.1.2
3
+ Version: 0.2.0
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>
@@ -102,19 +102,17 @@ the value from the start, even if the code changed while they waited.
102
102
  commands run once per `run` from the root: setup before the first run starts,
103
103
  teardown after the last one ends, even if runs failed, were stopped, or setup
104
104
  failed (so teardown must cope with a partial setup). A failing setup starts no
105
- run; a failing teardown fails the invocation. Both get `{invocation_dir}` only:
106
- the shared folder for a multi-run adhoc invocation, or the run folder when one
107
- run is planned. That folder exists before setup starts. In matrix mode with
108
- several runs there is no shared folder, so `{invocation_dir}` in a hook fails.
109
- `{run_dir}` is never filled in hooks; use `{invocation_dir}` instead. An
110
- unavailable placeholder fails before anything starts.
105
+ run; a failing teardown fails the invocation. Hooks and `meta` get no folder
106
+ placeholders: `{run_dir}` or `{invocation_dir}` in one fails before anything
107
+ starts. Share values through state instead.
111
108
  Setup can call `labnotes.set_state({"container": "abc"})`; runs and teardown read the
112
109
  dict with `labnotes.get_state()`. Writes merge by top-level key, replacing an
113
110
  existing value without dropping other keys. Values must be JSON serializable;
114
111
  reading before any write returns `{}`. Runs can only read: `set_state` raises
115
112
  `RuntimeError` there. Both functions raise outside an invocation and in `meta`.
116
113
  Labnotes gives setup/teardown `LABNOTES_STATE` and runs `LABNOTES_RUN_STATE`,
117
- pointing to `{invocation_dir}/state.json` or, without a shared folder, a temp
114
+ pointing to `state.json` in the multi-run adhoc group folder or the lone run's
115
+ folder, or, without a shared folder, a temp
118
116
  file kept through all runs and teardown and then removed. Queued runs and retries
119
117
  read the same file after setup finishes. `meta` receives neither state variable;
120
118
  state is never copied into `run.json`.
@@ -125,8 +123,8 @@ children and set their own hook and run variables.
125
123
  provides shared values; experiment values replace them by name. Variables in
126
124
  the environment that starts `labnotes experiments NAME run` win over both,
127
125
  including inline assignments. Values can use raw `{run_dir}` and
128
- `{invocation_dir}` placeholders. In hooks and meta, only `{invocation_dir}` is
129
- available, and it requires one planned run in matrix mode.
126
+ `{invocation_dir}` placeholders. Hooks and meta get neither, so values using
127
+ one are left out of their environment; runs still get them.
130
128
  `LABNOTES_*` names are reserved for labnotes.
131
129
  A run counts when it exited 0 and its `run.json` doesn't hold
132
130
  `"excluded": true`, so excluding a run by hand means adding that key; the next
@@ -0,0 +1,12 @@
1
+ from labnotes.runner.placeholders import PLACEHOLDERS
2
+
3
+
4
+ def check_hook_placeholders(hooks: dict[str, str | None]) -> None:
5
+ for hook, command in hooks.items():
6
+ for name in PLACEHOLDERS:
7
+ if command is not None and f"{{{name}}}" in command:
8
+ raise ValueError(
9
+ f"{hook} command {command} uses {{{name}}}; setup, teardown, "
10
+ "and meta get no folder placeholders. "
11
+ "Share values with set_state() and get_state()."
12
+ )
@@ -0,0 +1,23 @@
1
+ from collections.abc import Mapping
2
+
3
+ from labnotes.runner.fill_dirs import fill_dirs
4
+ from labnotes.runner.placeholders import PLACEHOLDERS
5
+
6
+
7
+ def fill_experiment_env(
8
+ configured: Mapping[str, str],
9
+ inherited: Mapping[str, str],
10
+ dirs: Mapping[str, str],
11
+ ) -> dict[str, str]:
12
+ caller = {
13
+ key: value
14
+ for key, value in inherited.items()
15
+ if not key.startswith("LABNOTES_")
16
+ }
17
+ filled = {
18
+ key: fill_dirs(value, dirs, quote=False)
19
+ for key, value in configured.items()
20
+ if key not in caller
21
+ and all(name in dirs for name in PLACEHOLDERS if f"{{{name}}}" in value)
22
+ }
23
+ return {**filled, **caller}
@@ -1,2 +1 @@
1
1
  PLACEHOLDERS = ("run_dir", "invocation_dir")
2
- HOOK_PLACEHOLDERS = ("invocation_dir",)
@@ -16,10 +16,10 @@ from labnotes.models import (
16
16
  Report,
17
17
  )
18
18
  from labnotes.retries import RETRY_EXIT_CODE, RETRY_SECONDS
19
+ from labnotes.runner.check_hook_placeholders import check_hook_placeholders
19
20
  from labnotes.runner.execute_run import execute_run
20
21
  from labnotes.runner.fill_experiment_env import fill_experiment_env
21
22
  from labnotes.runner.fill_dirs import fill_dirs
22
- from labnotes.runner.hook_dirs import hook_dirs
23
23
  from labnotes.runner.in_subset import in_subset
24
24
  from labnotes.runner.invocation_hook import invocation_hook
25
25
  from labnotes.runner.invocation_meta import invocation_meta
@@ -101,21 +101,21 @@ async def run_experiment(
101
101
  "teardown": experiment.teardown,
102
102
  "meta": experiment.meta,
103
103
  }
104
- dirs = hook_dirs(hooks, group_dir, run_dirs)
104
+ check_hook_placeholders(hooks)
105
105
  hook_env = {
106
- hook: fill_experiment_env(
107
- experiment.env, inherited, dirs, hook=hook, runs=len(plan)
108
- )
106
+ hook: fill_experiment_env(experiment.env, inherited, {})
109
107
  for hook, command in hooks.items()
110
108
  if command is not None
111
109
  }
112
110
  meta = None
113
111
  if experiment.meta is not None:
114
- meta = await invocation_meta(
115
- command_root, fill_dirs(experiment.meta, dirs), hook_env["meta"]
116
- )
117
- if dirs:
118
- Path(dirs["invocation_dir"]).mkdir(parents=True, exist_ok=True)
112
+ meta = await invocation_meta(command_root, experiment.meta, hook_env["meta"])
113
+ invocation_dir = group_dir
114
+ match run_dirs:
115
+ case [run_dir]:
116
+ invocation_dir = run_dir
117
+ if invocation_dir is not None:
118
+ Path(invocation_dir).mkdir(parents=True, exist_ok=True)
119
119
  runs = [{"run": run, "params": params} for run, params in plan]
120
120
  report(
121
121
  {
@@ -248,7 +248,7 @@ async def run_experiment(
248
248
  if snapshot is not None and snapshot.sha is not None:
249
249
  (folder / "git-sha.txt").write_text(snapshot.sha + "\n")
250
250
  (folder / "git-diff.patch").write_bytes(snapshot.diff or b"")
251
- run_invocation_dir = dirs.get("invocation_dir") or str(folder.resolve())
251
+ run_invocation_dir = invocation_dir or str(folder.resolve())
252
252
  report({"event": "started", "run": run, "params": params, "folder": folder})
253
253
  running[run] = set()
254
254
  trees[run] = set()
@@ -303,14 +303,14 @@ async def run_experiment(
303
303
  await invocation_hook(
304
304
  command_root,
305
305
  "teardown",
306
- fill_dirs(experiment.teardown, dirs),
306
+ experiment.teardown,
307
307
  hook_env["teardown"],
308
308
  )
309
309
 
310
310
  listen(Controls(interrupt, set_concurrency, kill, finish))
311
311
  with (
312
- nullcontext(dirs["invocation_dir"])
313
- if dirs
312
+ nullcontext(invocation_dir)
313
+ if invocation_dir is not None
314
314
  else TemporaryDirectory(prefix="labnotes-state-")
315
315
  ) as state_dir:
316
316
  state_file = Path(state_dir) / "state.json"
@@ -324,7 +324,7 @@ async def run_experiment(
324
324
  await invocation_hook(
325
325
  command_root,
326
326
  "setup",
327
- fill_dirs(experiment.setup, dirs),
327
+ experiment.setup,
328
328
  hook_env["setup"],
329
329
  )
330
330
  fill()
@@ -1,34 +0,0 @@
1
- from collections.abc import Mapping
2
-
3
- from labnotes.runner.fill_dirs import fill_dirs
4
- from labnotes.runner.placeholders import HOOK_PLACEHOLDERS, PLACEHOLDERS
5
-
6
-
7
- def fill_experiment_env(
8
- configured: Mapping[str, str],
9
- inherited: Mapping[str, str],
10
- dirs: Mapping[str, str],
11
- *,
12
- hook: str | None = None,
13
- runs: int = 1,
14
- ) -> dict[str, str]:
15
- caller = {
16
- key: value
17
- for key, value in inherited.items()
18
- if not key.startswith("LABNOTES_")
19
- }
20
- filled: dict[str, str] = {}
21
- for key, value in configured.items():
22
- if key in caller:
23
- continue
24
- used = [name for name in PLACEHOLDERS if f"{{{name}}}" in value]
25
- if hook is not None and any(name not in HOOK_PLACEHOLDERS for name in used):
26
- continue
27
- for name in used:
28
- if name not in dirs:
29
- raise ValueError(
30
- f"{hook} env.{key} uses {{{name}}}, but this invocation plans "
31
- f"{runs} runs. {{{name}}} needs exactly one run in this mode."
32
- )
33
- filled[key] = fill_dirs(value, dirs, quote=False)
34
- return {**filled, **caller}
@@ -1,21 +0,0 @@
1
- def hook_dirs(
2
- hooks: dict[str, str | None], invocation_dir: str | None, run_dirs: list[str]
3
- ) -> dict[str, str]:
4
- match run_dirs:
5
- case [run_dir]:
6
- invocation_dir = invocation_dir or run_dir
7
- available = {"invocation_dir": invocation_dir} if invocation_dir is not None else {}
8
- for hook, command in hooks.items():
9
- if command is None:
10
- continue
11
- if "{run_dir}" in command:
12
- raise ValueError(
13
- f"{hook} command {command} uses {{run_dir}}; "
14
- "use {invocation_dir} instead."
15
- )
16
- if "{invocation_dir}" in command and invocation_dir is None:
17
- raise ValueError(
18
- f"{hook} command {command} uses {{invocation_dir}}, but this "
19
- f"invocation plans {len(run_dirs)} runs and has no shared folder."
20
- )
21
- return available
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
File without changes
File without changes
File without changes