labnotes 0.0.138__tar.gz → 0.1.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.
- {labnotes-0.0.138 → labnotes-0.1.0}/PKG-INFO +1 -1
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/execute_run.py +4 -1
- labnotes-0.1.0/src/labnotes/runner/project_snapshot.py +118 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/run_experiment.py +15 -4
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/start_experiment.py +14 -11
- {labnotes-0.0.138 → labnotes-0.1.0}/.gitignore +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/CHANGELOG.md +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/MIGRATIONS.md +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/README.md +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/pyproject.toml +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/__main__.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/check_counts.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/compose_experiment.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/context.md +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/count_groups.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/create_app.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/create_note.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/experiments.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/file_changes.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/format_duration.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/format_started.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/get_state.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/grid_cell.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/group_runs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/init.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/invocation_started.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/is_non_negative_int.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/is_non_negative_number.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/is_positive_int.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/load_experiment.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/load_run.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/log.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/log_path.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/memory_interval.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/models.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/modes.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/nests_runs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/note_path.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/parse_subset.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/pivot_runs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/retries.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/run_output.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/cgroup_memory.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/check_args.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/check_subset.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/container_cgroup.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/container_memory.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/docker_output.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/exit_signal.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/fill_dirs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/fill_experiment_env.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/finalize_lost_runs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/flag_name.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/hook_dirs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/in_subset.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/invocation_hook.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/invocation_meta.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/new_param_run_id.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/new_stamped_run_id.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/param_run_name.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/prepare_experiment.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/read_experiment_config.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/record_memory.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/run_git.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/runs_parent.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/signal_tree.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/wait_for_tree.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/runs_view_command.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/save_conflict_copy.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/save_note.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/server.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/set_state.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/shared_params.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/state_path.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/elapsed.js +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/experiment.js +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/labnotes.css +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/local-time.js +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/new-note.js +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/note-edit.js +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/note-editor.js +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/run-output.js +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/summarize_config.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/experiment_run_ids.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/experiment_toml.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/experiments_ddl.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/file_text.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/note_text.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/open_tables.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/read_blocks.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/read_log.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/read_memory.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/read_run_file.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/runs_columns.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/templates/editor_imports.html +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/templates/experiment.html +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/templates/note.html +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/templates/run.html +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/browse_app.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/browse_tui.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/check_view_response.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/config.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/experiment_screen.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/experiments_screen.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/fill_table.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/format_cell.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/group_cells.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/invocation_columns.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/invocation_rows.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/leave_prompt.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/log_block.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/log_blocks.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/native_rows.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/reload_on_change.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/run_cells.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/run_output.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/run_screen.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/runs_table.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/runs_view.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/stopped_title.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/view_column.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/tui/view_process.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/utils/__init__.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/utils/format_bytes.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/utils/process_alive.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/utils/run_usage.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/utils/tree_rss.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/utils/tree_usage.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/view/__init__.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/view/answer.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/view/build.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/view/respond.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/view/serve.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/wait_for_note.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/src/labnotes/watch_rows.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/testing-conventions.toml +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/tests/conftest.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/tests/e2e/conftest.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/tests/integration/conftest.py +0 -0
- {labnotes-0.0.138 → labnotes-0.1.0}/uv.lock +0 -0
|
@@ -38,6 +38,7 @@ async def execute_run(
|
|
|
38
38
|
state_file: Path,
|
|
39
39
|
memory_interval: float,
|
|
40
40
|
environment: dict[str, str] | None = None,
|
|
41
|
+
git_info: dict[str, str | bool] | None = None,
|
|
41
42
|
) -> dict[str, object]:
|
|
42
43
|
resolved = shell_command(command, folder, params, args)
|
|
43
44
|
run: dict[str, object] = {
|
|
@@ -49,7 +50,9 @@ async def execute_run(
|
|
|
49
50
|
"error": None,
|
|
50
51
|
"ended_by": None,
|
|
51
52
|
"signal": None,
|
|
52
|
-
"git":
|
|
53
|
+
"git": git_info
|
|
54
|
+
if git_info is not None
|
|
55
|
+
else await asyncio.to_thread(run_git, root),
|
|
53
56
|
}
|
|
54
57
|
if args:
|
|
55
58
|
run["args"] = list(args)
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import os
|
|
3
|
+
import signal
|
|
4
|
+
import shutil
|
|
5
|
+
import subprocess
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from collections.abc import Iterator
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from tempfile import TemporaryDirectory
|
|
12
|
+
from types import FrameType
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class ProjectSnapshot:
|
|
17
|
+
root: Path
|
|
18
|
+
sha: str | None = None
|
|
19
|
+
diff: bytes | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def git(root: Path, *args: str) -> bytes:
|
|
23
|
+
return subprocess.check_output(["git", *args], cwd=root, stderr=subprocess.DEVNULL)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@contextmanager
|
|
27
|
+
def project_snapshot(root: Path) -> Iterator[ProjectSnapshot]:
|
|
28
|
+
"""Capture the working tree once for all commands in an invocation."""
|
|
29
|
+
try:
|
|
30
|
+
git(root, "rev-parse", "--is-inside-work-tree")
|
|
31
|
+
except (OSError, subprocess.SubprocessError):
|
|
32
|
+
logging.warning("Project is not a git repository; running from the live tree")
|
|
33
|
+
yield ProjectSnapshot(root)
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
try:
|
|
37
|
+
sha = git(root, "rev-parse", "HEAD").decode("ascii").strip()
|
|
38
|
+
diff = git(root, "diff", "HEAD")
|
|
39
|
+
except (OSError, subprocess.SubprocessError):
|
|
40
|
+
logging.warning("Project has no readable Git HEAD; running from the live tree")
|
|
41
|
+
yield ProjectSnapshot(root)
|
|
42
|
+
return
|
|
43
|
+
files = filter(
|
|
44
|
+
None, git(root, "ls-files", "-co", "--exclude-standard", "-z").split(b"\0")
|
|
45
|
+
)
|
|
46
|
+
ignored = filter(
|
|
47
|
+
None,
|
|
48
|
+
git(root, "ls-files", "-oi", "--exclude-standard", "--directory", "-z").split(
|
|
49
|
+
b"\0"
|
|
50
|
+
),
|
|
51
|
+
)
|
|
52
|
+
with TemporaryDirectory(prefix="labnotes-") as directory:
|
|
53
|
+
snapshot = Path(directory)
|
|
54
|
+
previous: dict[
|
|
55
|
+
signal.Signals,
|
|
56
|
+
Callable[[int, FrameType | None], object] | int | signal.Handlers | None,
|
|
57
|
+
] = {}
|
|
58
|
+
|
|
59
|
+
def stop(_number: int, _frame: object) -> None:
|
|
60
|
+
raise KeyboardInterrupt
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
for number in (signal.SIGTERM, signal.SIGHUP):
|
|
64
|
+
try:
|
|
65
|
+
old_handler = signal.signal(number, stop)
|
|
66
|
+
except ValueError:
|
|
67
|
+
break # Signals can only be installed from the main thread.
|
|
68
|
+
previous[number] = old_handler
|
|
69
|
+
subprocess.run(
|
|
70
|
+
[
|
|
71
|
+
"git",
|
|
72
|
+
"clone",
|
|
73
|
+
"--quiet",
|
|
74
|
+
"--shared",
|
|
75
|
+
"--no-checkout",
|
|
76
|
+
str(root),
|
|
77
|
+
str(snapshot),
|
|
78
|
+
],
|
|
79
|
+
check=True,
|
|
80
|
+
)
|
|
81
|
+
for raw in files:
|
|
82
|
+
relative = Path(raw.decode())
|
|
83
|
+
source = root / relative
|
|
84
|
+
if not source.exists() and not source.is_symlink():
|
|
85
|
+
continue
|
|
86
|
+
target = snapshot / relative
|
|
87
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
88
|
+
if source.is_symlink():
|
|
89
|
+
target.symlink_to(
|
|
90
|
+
os.readlink(source), target_is_directory=source.is_dir()
|
|
91
|
+
)
|
|
92
|
+
elif source.is_dir():
|
|
93
|
+
shutil.copytree(source, target, symlinks=True)
|
|
94
|
+
else:
|
|
95
|
+
shutil.copy2(source, target)
|
|
96
|
+
skipped: set[Path] = set()
|
|
97
|
+
for raw in ignored:
|
|
98
|
+
relative = Path(raw.decode().rstrip("/"))
|
|
99
|
+
if any(parent in relative.parents for parent in skipped):
|
|
100
|
+
continue
|
|
101
|
+
if any((snapshot / parent).is_symlink() for parent in relative.parents):
|
|
102
|
+
continue
|
|
103
|
+
source = root / relative
|
|
104
|
+
if source.is_dir() and (source / "pyvenv.cfg").exists():
|
|
105
|
+
skipped.add(relative)
|
|
106
|
+
continue
|
|
107
|
+
target = snapshot / relative
|
|
108
|
+
if target.is_symlink() or target.exists():
|
|
109
|
+
continue
|
|
110
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
111
|
+
target.symlink_to(source, target_is_directory=source.is_dir())
|
|
112
|
+
subprocess.run(
|
|
113
|
+
["git", "-C", str(snapshot), "reset", "--quiet", sha], check=True
|
|
114
|
+
)
|
|
115
|
+
finally:
|
|
116
|
+
for number, handler in previous.items():
|
|
117
|
+
signal.signal(number, handler)
|
|
118
|
+
yield ProjectSnapshot(snapshot, sha, diff)
|
|
@@ -27,6 +27,7 @@ from labnotes.runner.new_param_run_id import new_param_run_id
|
|
|
27
27
|
from labnotes.runner.new_stamped_run_id import new_stamped_run_id
|
|
28
28
|
from labnotes.runner.param_grid import param_grid
|
|
29
29
|
from labnotes.runner.plan_runs import plan_runs
|
|
30
|
+
from labnotes.runner.project_snapshot import ProjectSnapshot
|
|
30
31
|
from labnotes.runner.run_folder import run_folder
|
|
31
32
|
from labnotes.runner.signal_tree import signal_tree
|
|
32
33
|
from labnotes.state_path import STATE_ENV
|
|
@@ -49,7 +50,9 @@ async def run_experiment(
|
|
|
49
50
|
only: dict[str, str] | None = None,
|
|
50
51
|
args: list[str] | None = None,
|
|
51
52
|
environment: dict[str, str] | None = None,
|
|
53
|
+
snapshot: ProjectSnapshot | None = None,
|
|
52
54
|
) -> list[dict[str, object]]:
|
|
55
|
+
command_root = snapshot.root if snapshot is not None else config.root
|
|
53
56
|
inherited = dict(os.environ) if environment is None else environment
|
|
54
57
|
tables = open_tables(config)
|
|
55
58
|
existing = await experiment_run_ids(tables, name)
|
|
@@ -109,7 +112,7 @@ async def run_experiment(
|
|
|
109
112
|
meta = None
|
|
110
113
|
if experiment.meta is not None:
|
|
111
114
|
meta = await invocation_meta(
|
|
112
|
-
|
|
115
|
+
command_root, fill_dirs(experiment.meta, dirs), hook_env["meta"]
|
|
113
116
|
)
|
|
114
117
|
if dirs:
|
|
115
118
|
Path(dirs["invocation_dir"]).mkdir(parents=True, exist_ok=True)
|
|
@@ -242,12 +245,15 @@ async def run_experiment(
|
|
|
242
245
|
async def start(run: str, params: dict[str, object]) -> None:
|
|
243
246
|
folder = run_folder(config.root, config.runs, name, run)
|
|
244
247
|
folder.mkdir(parents=True, exist_ok=True)
|
|
248
|
+
if snapshot is not None and snapshot.sha is not None:
|
|
249
|
+
(folder / "git-sha.txt").write_text(snapshot.sha + "\n")
|
|
250
|
+
(folder / "git-diff.patch").write_bytes(snapshot.diff or b"")
|
|
245
251
|
run_invocation_dir = dirs.get("invocation_dir") or str(folder.resolve())
|
|
246
252
|
report({"event": "started", "run": run, "params": params, "folder": folder})
|
|
247
253
|
running[run] = set()
|
|
248
254
|
trees[run] = set()
|
|
249
255
|
result = await execute_run(
|
|
250
|
-
|
|
256
|
+
command_root,
|
|
251
257
|
folder,
|
|
252
258
|
fill_dirs(
|
|
253
259
|
experiment.command,
|
|
@@ -276,6 +282,11 @@ async def run_experiment(
|
|
|
276
282
|
"invocation_dir": run_invocation_dir,
|
|
277
283
|
},
|
|
278
284
|
),
|
|
285
|
+
git_info=(
|
|
286
|
+
{"commit": snapshot.sha, "dirty": bool(snapshot.diff)}
|
|
287
|
+
if snapshot is not None and snapshot.sha is not None
|
|
288
|
+
else None
|
|
289
|
+
),
|
|
279
290
|
)
|
|
280
291
|
del running[run], trees[run]
|
|
281
292
|
report({"event": "ended", "run": run, **result})
|
|
@@ -290,7 +301,7 @@ async def run_experiment(
|
|
|
290
301
|
if experiment.teardown is not None:
|
|
291
302
|
report({"event": "tearing down"})
|
|
292
303
|
await invocation_hook(
|
|
293
|
-
|
|
304
|
+
command_root,
|
|
294
305
|
"teardown",
|
|
295
306
|
fill_dirs(experiment.teardown, dirs),
|
|
296
307
|
hook_env["teardown"],
|
|
@@ -311,7 +322,7 @@ async def run_experiment(
|
|
|
311
322
|
if experiment.setup is not None:
|
|
312
323
|
report({"event": "setting up"})
|
|
313
324
|
await invocation_hook(
|
|
314
|
-
|
|
325
|
+
command_root,
|
|
315
326
|
"setup",
|
|
316
327
|
fill_dirs(experiment.setup, dirs),
|
|
317
328
|
hook_env["setup"],
|
|
@@ -3,6 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
from labnotes.models import LabnotesConfig, Listen, Report, Start
|
|
4
4
|
from labnotes.runner.finalize_lost_runs import finalize_lost_runs
|
|
5
5
|
from labnotes.runner.prepare_experiment import prepare_experiment
|
|
6
|
+
from labnotes.runner.project_snapshot import project_snapshot
|
|
6
7
|
from labnotes.runner.run_experiment import run_experiment
|
|
7
8
|
|
|
8
9
|
|
|
@@ -22,16 +23,18 @@ def start_experiment(
|
|
|
22
23
|
config, name, experiment_config, concurrency, only, args, setup, teardown
|
|
23
24
|
)
|
|
24
25
|
await finalize_lost_runs(config)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
26
|
+
with project_snapshot(config.root) as snapshot:
|
|
27
|
+
return await run_experiment(
|
|
28
|
+
config,
|
|
29
|
+
name,
|
|
30
|
+
experiment,
|
|
31
|
+
limit,
|
|
32
|
+
report,
|
|
33
|
+
listen,
|
|
34
|
+
timeout=timeout,
|
|
35
|
+
only=only,
|
|
36
|
+
args=args,
|
|
37
|
+
snapshot=snapshot,
|
|
38
|
+
)
|
|
36
39
|
|
|
37
40
|
return start
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|