labnotes 0.2.0__tar.gz → 0.2.2__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 (205) hide show
  1. {labnotes-0.2.0 → labnotes-0.2.2}/PKG-INFO +1 -1
  2. labnotes-0.2.2/src/labnotes/runner/check_args.py +19 -0
  3. labnotes-0.2.2/src/labnotes/runner/command_flags.py +35 -0
  4. labnotes-0.2.2/src/labnotes/runner/group_args.py +33 -0
  5. labnotes-0.2.2/src/labnotes/runner/override_flags.py +21 -0
  6. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/project_snapshot.py +11 -9
  7. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/shell_command.py +3 -1
  8. labnotes-0.2.0/src/labnotes/runner/check_args.py +0 -15
  9. {labnotes-0.2.0 → labnotes-0.2.2}/.gitignore +0 -0
  10. {labnotes-0.2.0 → labnotes-0.2.2}/CHANGELOG.md +0 -0
  11. {labnotes-0.2.0 → labnotes-0.2.2}/MIGRATIONS.md +0 -0
  12. {labnotes-0.2.0 → labnotes-0.2.2}/README.md +0 -0
  13. {labnotes-0.2.0 → labnotes-0.2.2}/pyproject.toml +0 -0
  14. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/__init__.py +0 -0
  15. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/__main__.py +0 -0
  16. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/asset_url.py +0 -0
  17. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/check_counts.py +0 -0
  18. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/cli.py +0 -0
  19. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/compose_experiment.py +0 -0
  20. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/context.md +0 -0
  21. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/context_guide.py +0 -0
  22. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/count_groups.py +0 -0
  23. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/count_runs.py +0 -0
  24. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/create_app.py +0 -0
  25. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/create_experiment.py +0 -0
  26. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/create_note.py +0 -0
  27. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/data_html.py +0 -0
  28. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/default_config_text.py +0 -0
  29. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/default_counts.py +0 -0
  30. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/drain_watch.py +0 -0
  31. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/experiment_notes.py +0 -0
  32. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/experiment_runs.py +0 -0
  33. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/experiments.py +0 -0
  34. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/file_changes.py +0 -0
  35. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/format_duration.py +0 -0
  36. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/format_started.py +0 -0
  37. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/format_value.py +0 -0
  38. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/get_state.py +0 -0
  39. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/grid_cell.py +0 -0
  40. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/group_runs.py +0 -0
  41. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/init.py +0 -0
  42. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/invocation_started.py +0 -0
  43. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/is_non_negative_int.py +0 -0
  44. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/is_non_negative_number.py +0 -0
  45. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/is_positive_int.py +0 -0
  46. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/list_experiments.py +0 -0
  47. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/list_runs.py +0 -0
  48. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/load_config.py +0 -0
  49. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/load_experiment.py +0 -0
  50. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/load_run.py +0 -0
  51. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/log.py +0 -0
  52. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/log_path.py +0 -0
  53. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/memory_interval.py +0 -0
  54. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/models.py +0 -0
  55. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/modes.py +0 -0
  56. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/nests_runs.py +0 -0
  57. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/note_path.py +0 -0
  58. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/notebook_kernel.py +0 -0
  59. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/output_html.py +0 -0
  60. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/parse_run_view.py +0 -0
  61. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/parse_subset.py +0 -0
  62. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/pivot_runs.py +0 -0
  63. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/print_rows.py +0 -0
  64. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/python_blocks.py +0 -0
  65. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/read_config.py +0 -0
  66. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/read_path.py +0 -0
  67. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/render_markdown.py +0 -0
  68. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/retries.py +0 -0
  69. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/run_output.py +0 -0
  70. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/__init__.py +0 -0
  71. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/cgroup_memory.py +0 -0
  72. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/check_hook_placeholders.py +0 -0
  73. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/check_subset.py +0 -0
  74. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/container_cgroup.py +0 -0
  75. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/container_memory.py +0 -0
  76. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/describe_event.py +0 -0
  77. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/docker_output.py +0 -0
  78. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/execute_run.py +0 -0
  79. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/exit_signal.py +0 -0
  80. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/fill_dirs.py +0 -0
  81. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/fill_experiment_env.py +0 -0
  82. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/finalize_lost_runs.py +0 -0
  83. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/flag_name.py +0 -0
  84. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/in_subset.py +0 -0
  85. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/invocation_hook.py +0 -0
  86. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/invocation_meta.py +0 -0
  87. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/new_param_run_id.py +0 -0
  88. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/new_run_id.py +0 -0
  89. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/new_stamped_run_id.py +0 -0
  90. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/param_args.py +0 -0
  91. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/param_grid.py +0 -0
  92. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/param_run_name.py +0 -0
  93. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/placeholders.py +0 -0
  94. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/plan_runs.py +0 -0
  95. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/prepare_experiment.py +0 -0
  96. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/read_experiment_config.py +0 -0
  97. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/record_memory.py +0 -0
  98. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/run_experiment.py +0 -0
  99. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/run_folder.py +0 -0
  100. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/run_git.py +0 -0
  101. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/run_headless.py +0 -0
  102. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/runs_parent.py +0 -0
  103. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/signal_tree.py +0 -0
  104. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/start_experiment.py +0 -0
  105. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/stop_then_kill.py +0 -0
  106. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/wait_for_tree.py +0 -0
  107. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runner/write_run_json.py +0 -0
  108. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/runs_view_command.py +0 -0
  109. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/save_conflict_copy.py +0 -0
  110. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/save_note.py +0 -0
  111. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/saved_outputs.py +0 -0
  112. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/serve_app.py +0 -0
  113. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/server.py +0 -0
  114. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/set_state.py +0 -0
  115. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/shared_params.py +0 -0
  116. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/sort_href.py +0 -0
  117. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/sql_quote.py +0 -0
  118. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/state_path.py +0 -0
  119. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/elapsed.js +0 -0
  120. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/experiment.js +0 -0
  121. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/labnotes.css +0 -0
  122. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/local-time.js +0 -0
  123. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/new-note.js +0 -0
  124. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/note-edit.js +0 -0
  125. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/note-editor.js +0 -0
  126. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/row-link.js +0 -0
  127. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/run-cell.js +0 -0
  128. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/run-output.js +0 -0
  129. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/static/view-toggle.js +0 -0
  130. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/summarize_config.py +0 -0
  131. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/__init__.py +0 -0
  132. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/experiment_rows.py +0 -0
  133. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/experiment_run_ids.py +0 -0
  134. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/experiment_toml.py +0 -0
  135. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/experiments_ddl.py +0 -0
  136. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/file_prefix.py +0 -0
  137. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/file_text.py +0 -0
  138. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/iso_time.py +0 -0
  139. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/list_experiment_runs.py +0 -0
  140. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/note_text.py +0 -0
  141. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/open_tables.py +0 -0
  142. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/path_glob.py +0 -0
  143. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/path_pattern.py +0 -0
  144. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/read_blocks.py +0 -0
  145. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/read_experiment.py +0 -0
  146. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/read_file.py +0 -0
  147. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/read_log.py +0 -0
  148. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/read_memory.py +0 -0
  149. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/read_run.py +0 -0
  150. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/read_run_file.py +0 -0
  151. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/runs_columns.py +0 -0
  152. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tables/summarize_experiments.py +0 -0
  153. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/templates/base.html +0 -0
  154. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/templates/editor_imports.html +0 -0
  155. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/templates/experiment.html +0 -0
  156. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/templates/index.html +0 -0
  157. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/templates/note.html +0 -0
  158. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/templates/run.html +0 -0
  159. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/__init__.py +0 -0
  160. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/browse_app.py +0 -0
  161. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/browse_tui.py +0 -0
  162. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/check_view_response.py +0 -0
  163. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/config.py +0 -0
  164. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/dim.py +0 -0
  165. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/experiment_screen.py +0 -0
  166. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/experiments_screen.py +0 -0
  167. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/fill_table.py +0 -0
  168. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/format_cell.py +0 -0
  169. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/format_elapsed.py +0 -0
  170. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/group_cells.py +0 -0
  171. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/invocation_columns.py +0 -0
  172. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/invocation_rows.py +0 -0
  173. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/leave_prompt.py +0 -0
  174. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/log_block.py +0 -0
  175. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/log_blocks.py +0 -0
  176. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/native_rows.py +0 -0
  177. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/reload_on_change.py +0 -0
  178. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/run_app.py +0 -0
  179. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/run_cells.py +0 -0
  180. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/run_output.py +0 -0
  181. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/run_screen.py +0 -0
  182. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/run_tui.py +0 -0
  183. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/runs_table.py +0 -0
  184. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/runs_view.py +0 -0
  185. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/stopped_title.py +0 -0
  186. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/view_column.py +0 -0
  187. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/tui/view_process.py +0 -0
  188. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/utils/__init__.py +0 -0
  189. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/utils/format_bytes.py +0 -0
  190. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/utils/process_alive.py +0 -0
  191. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/utils/run_usage.py +0 -0
  192. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/utils/tree_rss.py +0 -0
  193. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/utils/tree_usage.py +0 -0
  194. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/view/__init__.py +0 -0
  195. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/view/answer.py +0 -0
  196. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/view/build.py +0 -0
  197. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/view/respond.py +0 -0
  198. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/view/serve.py +0 -0
  199. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/wait_for_note.py +0 -0
  200. {labnotes-0.2.0 → labnotes-0.2.2}/src/labnotes/watch_rows.py +0 -0
  201. {labnotes-0.2.0 → labnotes-0.2.2}/testing-conventions.toml +0 -0
  202. {labnotes-0.2.0 → labnotes-0.2.2}/tests/conftest.py +0 -0
  203. {labnotes-0.2.0 → labnotes-0.2.2}/tests/e2e/conftest.py +0 -0
  204. {labnotes-0.2.0 → labnotes-0.2.2}/tests/integration/conftest.py +0 -0
  205. {labnotes-0.2.0 → labnotes-0.2.2}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labnotes
3
- Version: 0.2.0
3
+ Version: 0.2.2
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>
@@ -0,0 +1,19 @@
1
+ from collections import Counter
2
+
3
+ from labnotes.runner.command_flags import command_flags
4
+ from labnotes.runner.group_args import group_args
5
+
6
+
7
+ def check_args(params: dict[str, list[object]], command: str, args: list[str]) -> None:
8
+ taken = Counter(flag.name for flag in command_flags(command))
9
+ given: set[str] = set()
10
+ for name in (arg.name for arg in group_args(args) if arg.name is not None):
11
+ if name in params:
12
+ raise ValueError(f"--{name} is a param. Pick it with --{name}=VALUE.")
13
+ if taken[name] > 1:
14
+ raise ValueError(
15
+ f"--{name} is in the command more than once, so -- can't override it."
16
+ )
17
+ if taken[name] and name in given:
18
+ raise ValueError(f"--{name} is in -- ARGS more than once.")
19
+ given.add(name)
@@ -0,0 +1,35 @@
1
+ import re
2
+ from itertools import zip_longest
3
+ from typing import NamedTuple
4
+
5
+ from labnotes.runner.flag_name import flag_name
6
+
7
+ OPERATOR = ";&|<>()"
8
+ WORD = re.compile(
9
+ rf"""[{OPERATOR}]+|(?:[^\s{OPERATOR}'"\\]|\\.|'[^']*'|"(?:[^"\\]|\\.)*")+""",
10
+ re.DOTALL,
11
+ )
12
+
13
+
14
+ class CommandFlag(NamedTuple):
15
+ name: str
16
+ start: int
17
+ end: int
18
+ joined: bool
19
+
20
+
21
+ def command_flags(command: str) -> list[CommandFlag]:
22
+ words = list(WORD.finditer(command))
23
+ flags: list[CommandFlag] = []
24
+ for word, after in zip_longest(words, words[1:]):
25
+ name = flag_name(word[0])
26
+ if name is None:
27
+ continue
28
+ joined = "=" in word[0]
29
+ end = word.end()
30
+ if not joined and after is not None:
31
+ value = after[0]
32
+ if value[0] not in OPERATOR and not value.startswith("-"):
33
+ end = after.end()
34
+ flags.append(CommandFlag(name, word.start(), end, joined))
35
+ return flags
@@ -0,0 +1,33 @@
1
+ from typing import NamedTuple
2
+
3
+ from labnotes.runner.flag_name import flag_name
4
+
5
+
6
+ class Arg(NamedTuple):
7
+ name: str | None
8
+ value: str | None
9
+ tokens: list[str]
10
+
11
+
12
+ def group_args(args: list[str]) -> list[Arg]:
13
+ grouped: list[Arg] = []
14
+ values = {
15
+ index + 1
16
+ for index, token in enumerate(args[:-1])
17
+ if flag_name(token) is not None
18
+ and "=" not in token
19
+ and not args[index + 1].startswith("-")
20
+ }
21
+ for index, token in enumerate(args):
22
+ name = flag_name(token)
23
+ if index in values:
24
+ continue
25
+ if name is None:
26
+ grouped.append(Arg(None, None, [token]))
27
+ elif "=" in token:
28
+ grouped.append(Arg(name, token.partition("=")[2], [token]))
29
+ elif index + 1 in values:
30
+ grouped.append(Arg(name, args[index + 1], [token, args[index + 1]]))
31
+ else:
32
+ grouped.append(Arg(name, None, [token]))
33
+ return grouped
@@ -0,0 +1,21 @@
1
+ import shlex
2
+
3
+ from labnotes.runner.command_flags import command_flags
4
+ from labnotes.runner.group_args import group_args
5
+
6
+
7
+ def override_flags(command: str, args: list[str]) -> tuple[str, list[str]]:
8
+ flags = {flag.name: flag for flag in command_flags(command)}
9
+ replaced: list[tuple[int, int, str]] = []
10
+ rest: list[str] = []
11
+ for arg in group_args(args):
12
+ flag = flags.get(arg.name) if arg.name is not None else None
13
+ if flag is None:
14
+ rest += arg.tokens
15
+ elif arg.value is not None:
16
+ separator = "=" if flag.joined else " "
17
+ text = f"--{arg.name}{separator}{shlex.quote(arg.value)}"
18
+ replaced.append((flag.start, flag.end, text))
19
+ for start, end, text in sorted(replaced, reverse=True):
20
+ command = command[:start] + text + command[end:]
21
+ return command, rest
@@ -35,19 +35,21 @@ def project_snapshot(root: Path) -> Iterator[ProjectSnapshot]:
35
35
 
36
36
  try:
37
37
  sha = git(root, "rev-parse", "HEAD").decode("ascii").strip()
38
- diff = git(root, "diff", "HEAD")
38
+ repo_root = Path(git(root, "rev-parse", "--show-toplevel").decode().strip())
39
+ project_prefix = root.resolve().relative_to(repo_root.resolve())
40
+ diff = git(repo_root, "diff", "HEAD")
39
41
  except (OSError, subprocess.SubprocessError):
40
42
  logging.warning("Project has no readable Git HEAD; running from the live tree")
41
43
  yield ProjectSnapshot(root)
42
44
  return
43
45
  files = filter(
44
- None, git(root, "ls-files", "-co", "--exclude-standard", "-z").split(b"\0")
46
+ None, git(repo_root, "ls-files", "-co", "--exclude-standard", "-z").split(b"\0")
45
47
  )
46
48
  ignored = filter(
47
49
  None,
48
- git(root, "ls-files", "-oi", "--exclude-standard", "--directory", "-z").split(
49
- b"\0"
50
- ),
50
+ git(
51
+ repo_root, "ls-files", "-oi", "--exclude-standard", "--directory", "-z"
52
+ ).split(b"\0"),
51
53
  )
52
54
  with TemporaryDirectory(prefix="labnotes-") as directory:
53
55
  snapshot = Path(directory)
@@ -73,14 +75,14 @@ def project_snapshot(root: Path) -> Iterator[ProjectSnapshot]:
73
75
  "--quiet",
74
76
  "--shared",
75
77
  "--no-checkout",
76
- str(root),
78
+ str(repo_root),
77
79
  str(snapshot),
78
80
  ],
79
81
  check=True,
80
82
  )
81
83
  for raw in files:
82
84
  relative = Path(raw.decode())
83
- source = root / relative
85
+ source = repo_root / relative
84
86
  if not source.exists() and not source.is_symlink():
85
87
  continue
86
88
  target = snapshot / relative
@@ -100,7 +102,7 @@ def project_snapshot(root: Path) -> Iterator[ProjectSnapshot]:
100
102
  continue
101
103
  if any((snapshot / parent).is_symlink() for parent in relative.parents):
102
104
  continue
103
- source = root / relative
105
+ source = repo_root / relative
104
106
  if source.is_dir() and (source / "pyvenv.cfg").exists():
105
107
  skipped.add(relative)
106
108
  continue
@@ -115,4 +117,4 @@ def project_snapshot(root: Path) -> Iterator[ProjectSnapshot]:
115
117
  finally:
116
118
  for number, handler in previous.items():
117
119
  signal.signal(number, handler)
118
- yield ProjectSnapshot(snapshot, sha, diff)
120
+ yield ProjectSnapshot(snapshot / project_prefix, sha, diff)
@@ -2,6 +2,7 @@ import shlex
2
2
  from collections.abc import Sequence
3
3
  from pathlib import Path
4
4
 
5
+ from labnotes.runner.override_flags import override_flags
5
6
  from labnotes.runner.param_args import param_args
6
7
 
7
8
 
@@ -9,4 +10,5 @@ def shell_command(
9
10
  command: str, folder: Path, params: dict[str, object], args: Sequence[str] = ()
10
11
  ) -> str:
11
12
  filled = command.replace("{run_dir}", shlex.quote(str(folder)))
12
- return f"{filled} {shlex.join([*param_args(params), *args])}".rstrip()
13
+ overridden, rest = override_flags(filled, list(args))
14
+ return f"{overridden} {shlex.join([*param_args(params), *rest])}".rstrip()
@@ -1,15 +0,0 @@
1
- import shlex
2
-
3
- from labnotes.runner.flag_name import flag_name
4
-
5
-
6
- def check_args(params: dict[str, list[object]], command: str, args: list[str]) -> None:
7
- taken = {flag_name(token) for token in shlex.split(command)}
8
- for arg in args:
9
- name = flag_name(arg)
10
- if name is None:
11
- continue
12
- if name in params:
13
- raise ValueError(f"--{name} is a param. Pick it with --{name}=VALUE.")
14
- if name in taken:
15
- raise ValueError(f"--{name} is already in the command.")
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