labnotes 0.0.111__tar.gz → 0.0.112__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.111 → labnotes-0.0.112}/PKG-INFO +1 -1
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/context.md +7 -0
- labnotes-0.0.112/src/labnotes/is_non_negative_number.py +10 -0
- labnotes-0.0.112/src/labnotes/memory_interval.py +2 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/models.py +2 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/execute_run.py +15 -1
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/read_experiment_config.py +16 -1
- labnotes-0.0.112/src/labnotes/runner/record_memory.py +18 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/run_experiment.py +1 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/open_tables.py +15 -0
- labnotes-0.0.112/src/labnotes/tables/read_memory.py +34 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/utils/process_alive.py +1 -1
- labnotes-0.0.112/src/labnotes/utils/tree_rss.py +6 -0
- labnotes-0.0.111/src/labnotes/utils/tree_rss.py → labnotes-0.0.112/src/labnotes/utils/tree_usage.py +2 -2
- {labnotes-0.0.111 → labnotes-0.0.112}/.gitignore +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/CHANGELOG.md +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/MIGRATIONS.md +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/README.md +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/pyproject.toml +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/__main__.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/check_counts.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/compose_experiment.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/count_groups.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/create_app.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/create_note.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/__init__.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/control_run.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/daemon_app.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/daemon_client.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/daemon_running.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/daemon_server.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/describe_status.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/ensure_daemon.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/exit_when_idle.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/finalize_lost_runs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/remote_start.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/run_daemon.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/run_logs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/serve_daemon.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/socket_path.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/stop_runs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/watch_daemon.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon/watch_status.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/daemon_command.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/experiments.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/file_changes.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/format_duration.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/format_started.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/group_runs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/init.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/invocation_started.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/is_non_negative_int.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/is_positive_int.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/load_experiment.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/load_run.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/log.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/log_path.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/modes.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/nests_runs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/note_path.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/retries.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/run_command.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/run_control.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/run_output.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/run_status.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/exit_signal.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/hold_folder.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/invocation_meta.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/new_param_run_id.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/new_stamped_run_id.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/param_run_name.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/prepare_experiment.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/runs_parent.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/signal_tree.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/wait_for_tree.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/save_conflict_copy.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/save_note.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/server.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/shared_params.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/elapsed.js +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/experiment.js +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/labnotes.css +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/local-time.js +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/new-note.js +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/note-edit.js +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/note-editor.js +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/run-output.js +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/summarize_config.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/experiment_run_ids.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/experiment_toml.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/experiments_ddl.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/file_text.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/note_text.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/read_blocks.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/read_log.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/templates/editor_imports.html +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/templates/experiment.html +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/templates/note.html +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/templates/run.html +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/browse_app.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/browse_tui.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/config.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/experiment_screen.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/experiments_screen.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/fill_table.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/group_cells.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/invocation_columns.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/invocation_rows.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/leave_prompt.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/log_block.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/log_blocks.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/reload_on_change.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/run_cells.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/run_output.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/run_screen.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/runs_table.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/tui/stopped_title.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/utils/__init__.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/utils/format_bytes.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/utils/run_usage.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/wait_for_note.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/src/labnotes/watch_rows.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/testing-conventions.toml +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/tests/integration/conftest.py +0 -0
- {labnotes-0.0.111 → labnotes-0.0.112}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: labnotes
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.112
|
|
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>
|
|
@@ -123,6 +123,13 @@ shows a waiting retry as `retry in 30s` and the end as `gave up (exit 75)`;
|
|
|
123
123
|
|
|
124
124
|
Runs can take a long time. Start `run` in the background, then check on it with `labnotes experiments foo runs --format json`; a run with no `ended` is still going. Each run lists `size`, the bytes in its run folder, and a running run lists `memory`, the resident bytes of its command and everything it started.
|
|
125
125
|
|
|
126
|
+
While a run goes, labnotes appends a memory sample to `memory.jsonl` in its run
|
|
127
|
+
folder every 5 seconds: one JSON line, `{"time": <unix seconds>, "rss": <bytes>,
|
|
128
|
+
"processes": <count>}`, for the command and everything it started. It keeps
|
|
129
|
+
every sample, so a run killed for memory still shows the climb.
|
|
130
|
+
`memory_interval` in `experiment.toml` (or `[experiment]` in `.labnotes.toml`)
|
|
131
|
+
sets the seconds between samples; 0 turns it off.
|
|
132
|
+
|
|
126
133
|
In a terminal, `run` shows a live table of runs (status, time, memory and
|
|
127
134
|
folder size) and stays
|
|
128
135
|
open when they finish. Failed runs show their status in red; `f` hides or shows them, and `r` on one
|
|
@@ -4,6 +4,7 @@ from pathlib import Path
|
|
|
4
4
|
from typing import Any, Protocol
|
|
5
5
|
|
|
6
6
|
from labnotes.default_counts import DEFAULT_COUNTS
|
|
7
|
+
from labnotes.memory_interval import DEFAULT_MEMORY_INTERVAL
|
|
7
8
|
from labnotes.modes import DEFAULT_MODE
|
|
8
9
|
from labnotes.retries import DEFAULT_RETRIES
|
|
9
10
|
|
|
@@ -66,3 +67,4 @@ class ExperimentConfig:
|
|
|
66
67
|
concurrency: int = 1
|
|
67
68
|
retries: int = DEFAULT_RETRIES
|
|
68
69
|
meta: str | None = None
|
|
70
|
+
memory_interval: float = DEFAULT_MEMORY_INTERVAL
|
|
@@ -9,6 +9,7 @@ import psutil
|
|
|
9
9
|
|
|
10
10
|
from labnotes.log_path import LOG_ENV
|
|
11
11
|
from labnotes.runner.exit_signal import exit_signal
|
|
12
|
+
from labnotes.runner.record_memory import record_memory
|
|
12
13
|
from labnotes.runner.shell_command import shell_command
|
|
13
14
|
from labnotes.runner.stop_then_kill import GRACE_SECONDS
|
|
14
15
|
from labnotes.runner.wait_for_tree import wait_for_tree
|
|
@@ -30,6 +31,8 @@ async def execute_run(
|
|
|
30
31
|
stop: Callable[[], None] = lambda: None,
|
|
31
32
|
stopped: Callable[[], bool] = lambda: False,
|
|
32
33
|
meta: dict[str, object] | None = None,
|
|
34
|
+
*,
|
|
35
|
+
memory_interval: float,
|
|
33
36
|
) -> dict[str, object]:
|
|
34
37
|
resolved = shell_command(command, folder, params)
|
|
35
38
|
run: dict[str, object] = {
|
|
@@ -69,9 +72,17 @@ async def execute_run(
|
|
|
69
72
|
else:
|
|
70
73
|
running.add(process)
|
|
71
74
|
run["pid"] = process.pid
|
|
75
|
+
created = None
|
|
72
76
|
with contextlib.suppress(psutil.Error):
|
|
73
|
-
run["pid_created"] = psutil.Process(process.pid).create_time()
|
|
77
|
+
run["pid_created"] = created = psutil.Process(process.pid).create_time()
|
|
74
78
|
write_run_json(folder, run)
|
|
79
|
+
sampler = None
|
|
80
|
+
if memory_interval > 0 and created is not None:
|
|
81
|
+
sampler = asyncio.create_task(
|
|
82
|
+
record_memory(
|
|
83
|
+
folder / "memory.jsonl", process.pid, created, memory_interval
|
|
84
|
+
)
|
|
85
|
+
)
|
|
75
86
|
exited = asyncio.ensure_future(process.wait())
|
|
76
87
|
kill_later = None
|
|
77
88
|
try:
|
|
@@ -100,6 +111,9 @@ async def execute_run(
|
|
|
100
111
|
finally:
|
|
101
112
|
if kill_later is not None:
|
|
102
113
|
kill_later.cancel()
|
|
114
|
+
if sampler is not None:
|
|
115
|
+
sampler.cancel()
|
|
116
|
+
await asyncio.wait({sampler})
|
|
103
117
|
running.discard(process)
|
|
104
118
|
run["ended"] = iso_time(time.time())
|
|
105
119
|
write_run_json(folder, run)
|
|
@@ -7,6 +7,8 @@ from labnotes.compose_experiment import compose_experiment
|
|
|
7
7
|
from labnotes.default_counts import DEFAULT_COUNTS
|
|
8
8
|
from labnotes.models import ExperimentConfig
|
|
9
9
|
from labnotes.is_non_negative_int import is_non_negative_int
|
|
10
|
+
from labnotes.is_non_negative_number import is_non_negative_number
|
|
11
|
+
from labnotes.memory_interval import DEFAULT_MEMORY_INTERVAL
|
|
10
12
|
from labnotes.is_positive_int import is_positive_int
|
|
11
13
|
from labnotes.modes import MODES
|
|
12
14
|
|
|
@@ -20,6 +22,7 @@ SETTINGS = {
|
|
|
20
22
|
"concurrency",
|
|
21
23
|
"retries",
|
|
22
24
|
"meta",
|
|
25
|
+
"memory_interval",
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
|
|
@@ -73,6 +76,18 @@ def read_experiment_config(
|
|
|
73
76
|
meta = values.get("meta")
|
|
74
77
|
if meta is not None and (not isinstance(meta, str) or not meta.strip()):
|
|
75
78
|
raise ValueError(f"{path}: meta must be a string")
|
|
79
|
+
memory_interval = values.get("memory_interval", DEFAULT_MEMORY_INTERVAL)
|
|
80
|
+
if not is_non_negative_number(memory_interval):
|
|
81
|
+
raise ValueError(f"{path}: memory_interval must be 0 or more seconds")
|
|
76
82
|
return ExperimentConfig(
|
|
77
|
-
command,
|
|
83
|
+
command,
|
|
84
|
+
repeats,
|
|
85
|
+
params,
|
|
86
|
+
name,
|
|
87
|
+
counts,
|
|
88
|
+
mode,
|
|
89
|
+
concurrency,
|
|
90
|
+
retries,
|
|
91
|
+
meta,
|
|
92
|
+
memory_interval,
|
|
78
93
|
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
import time
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from labnotes.utils.tree_usage import tree_usage
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def record_memory(path: Path, pid: int, created: float, interval: float) -> None:
|
|
10
|
+
with path.open("a", encoding="utf-8") as samples:
|
|
11
|
+
while True:
|
|
12
|
+
usage = tree_usage(pid, created)
|
|
13
|
+
if usage is not None:
|
|
14
|
+
rss, processes = usage
|
|
15
|
+
sample = {"time": time.time(), "rss": rss, "processes": processes}
|
|
16
|
+
samples.write(json.dumps(sample) + "\n")
|
|
17
|
+
samples.flush()
|
|
18
|
+
await asyncio.sleep(interval)
|
|
@@ -10,6 +10,7 @@ from labnotes.tables.read_blocks import read_blocks
|
|
|
10
10
|
from labnotes.tables.read_experiment import read_experiment
|
|
11
11
|
from labnotes.tables.read_file import read_file
|
|
12
12
|
from labnotes.tables.read_log import read_log
|
|
13
|
+
from labnotes.tables.read_memory import read_memory
|
|
13
14
|
from labnotes.tables.read_run import read_run
|
|
14
15
|
|
|
15
16
|
FILES = """
|
|
@@ -39,6 +40,12 @@ CREATE TABLE blocks (
|
|
|
39
40
|
PRIMARY KEY (experiment, run)
|
|
40
41
|
)
|
|
41
42
|
"""
|
|
43
|
+
MEMORY = """
|
|
44
|
+
CREATE TABLE memory (
|
|
45
|
+
experiment TEXT, run TEXT, line INTEGER, time REAL, rss INTEGER, processes INTEGER,
|
|
46
|
+
PRIMARY KEY (experiment, run, line)
|
|
47
|
+
)
|
|
48
|
+
"""
|
|
42
49
|
|
|
43
50
|
|
|
44
51
|
def open_tables(config: LabnotesConfig) -> DirSQL:
|
|
@@ -78,6 +85,14 @@ def open_tables(config: LabnotesConfig) -> DirSQL:
|
|
|
78
85
|
read_blocks, root, config.runs, experiment=config.experiment
|
|
79
86
|
),
|
|
80
87
|
),
|
|
88
|
+
Table(
|
|
89
|
+
name="memory",
|
|
90
|
+
ddl=MEMORY,
|
|
91
|
+
glob=path_glob(f"{config.runs}/memory.jsonl"),
|
|
92
|
+
on_file=partial(
|
|
93
|
+
read_memory, root, config.runs, experiment=config.experiment
|
|
94
|
+
),
|
|
95
|
+
),
|
|
81
96
|
Table(
|
|
82
97
|
name="experiment_configs",
|
|
83
98
|
ddl=experiments_ddl(config),
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from labnotes.tables.path_pattern import path_pattern
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def read_memory(
|
|
8
|
+
root: Path, template: str, path: str, experiment: str | None = None
|
|
9
|
+
) -> list[dict[str, object]]:
|
|
10
|
+
relative = Path(path).relative_to(root).as_posix()
|
|
11
|
+
found = path_pattern(f"{template}/memory.jsonl").fullmatch(relative)
|
|
12
|
+
if found is None:
|
|
13
|
+
return []
|
|
14
|
+
rows: list[dict[str, object]] = []
|
|
15
|
+
lines = Path(path).read_text(encoding="utf-8").splitlines()
|
|
16
|
+
for line, text in enumerate(lines):
|
|
17
|
+
# A run killed mid-write leaves a cut-off last line.
|
|
18
|
+
try:
|
|
19
|
+
sample = json.loads(text)
|
|
20
|
+
except json.JSONDecodeError:
|
|
21
|
+
continue
|
|
22
|
+
if not isinstance(sample, dict):
|
|
23
|
+
continue
|
|
24
|
+
rows.append(
|
|
25
|
+
{
|
|
26
|
+
"experiment": experiment or found["experiment"],
|
|
27
|
+
"run": found["run"],
|
|
28
|
+
"line": line,
|
|
29
|
+
"time": sample.get("time"),
|
|
30
|
+
"rss": sample.get("rss"),
|
|
31
|
+
"processes": sample.get("processes"),
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
return rows
|
labnotes-0.0.111/src/labnotes/utils/tree_rss.py → labnotes-0.0.112/src/labnotes/utils/tree_usage.py
RENAMED
|
@@ -7,7 +7,7 @@ import psutil
|
|
|
7
7
|
SAME_START_SECONDS = 0.01
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def
|
|
10
|
+
def tree_usage(pid: int, created: float) -> tuple[int, int] | None:
|
|
11
11
|
try:
|
|
12
12
|
root = psutil.Process(pid)
|
|
13
13
|
if abs(root.create_time() - created) > SAME_START_SECONDS:
|
|
@@ -24,4 +24,4 @@ def tree_rss(pid: int, created: float) -> int | None:
|
|
|
24
24
|
for process in tree:
|
|
25
25
|
with contextlib.suppress(psutil.NoSuchProcess, psutil.AccessDenied):
|
|
26
26
|
sizes.append(process.memory_info().rss)
|
|
27
|
-
return sum(sizes) if sizes else None
|
|
27
|
+
return (sum(sizes), len(sizes)) if sizes else None
|
|
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
|