labnotes 0.0.104__tar.gz → 0.0.106__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.104 → labnotes-0.0.106}/PKG-INFO +1 -1
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/context.md +7 -3
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/execute_run.py +12 -1
- labnotes-0.0.106/src/labnotes/runner/exit_signal.py +11 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/run_experiment.py +1 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/run_output.py +17 -4
- {labnotes-0.0.104 → labnotes-0.0.106}/testing-conventions.toml +6 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/.gitignore +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/CHANGELOG.md +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/MIGRATIONS.md +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/README.md +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/pyproject.toml +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/__init__.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/__main__.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/asset_url.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/check_counts.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/cli.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/compose_experiment.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/context_guide.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/count_groups.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/count_runs.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/create_app.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/create_experiment.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/create_note.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/__init__.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/control_run.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/daemon_app.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/daemon_client.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/daemon_running.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/daemon_server.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/describe_status.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/ensure_daemon.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/exit_when_idle.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/remote_start.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/run_daemon.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/run_logs.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/serve_daemon.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/socket_path.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/stop_runs.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/watch_daemon.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon/watch_status.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/daemon_command.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/data_html.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/default_config_text.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/default_counts.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/drain_watch.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/experiment_notes.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/experiment_runs.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/experiments.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/file_changes.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/format_duration.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/format_started.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/format_value.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/group_runs.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/init.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/invocation_started.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/is_non_negative_int.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/is_positive_int.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/list_experiments.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/list_runs.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/load_config.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/load_experiment.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/load_run.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/log.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/log_path.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/models.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/modes.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/nests_runs.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/note_path.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/notebook_kernel.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/output_html.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/parse_run_view.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/print_rows.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/python_blocks.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/read_config.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/read_path.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/render_markdown.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/retries.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/run_command.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/run_control.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/run_output.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/run_status.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/__init__.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/describe_event.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/hold_folder.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/new_param_run_id.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/new_run_id.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/new_stamped_run_id.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/param_args.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/param_grid.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/param_run_name.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/plan_runs.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/prepare_experiment.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/read_experiment_config.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/run_folder.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/run_headless.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/runs_parent.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/shell_command.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/signal_tree.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/stop_then_kill.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/wait_for_tree.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/runner/write_run_json.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/save_conflict_copy.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/save_note.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/saved_outputs.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/serve_app.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/server.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/shared_params.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/sort_href.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/sql_quote.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/elapsed.js +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/experiment.js +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/labnotes.css +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/local-time.js +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/new-note.js +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/note-edit.js +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/note-editor.js +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/row-link.js +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/run-cell.js +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/run-output.js +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/static/view-toggle.js +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/summarize_config.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/__init__.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/experiment_rows.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/experiment_run_ids.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/experiment_toml.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/experiments_ddl.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/file_prefix.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/file_text.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/iso_time.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/list_experiment_runs.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/note_text.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/open_tables.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/path_glob.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/path_pattern.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/read_blocks.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/read_experiment.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/read_file.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/read_log.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/read_run.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tables/summarize_experiments.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/templates/base.html +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/templates/editor_imports.html +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/templates/experiment.html +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/templates/index.html +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/templates/note.html +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/templates/run.html +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/__init__.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/browse_app.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/browse_tui.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/dim.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/experiment_screen.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/experiments_screen.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/fill_table.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/format_elapsed.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/group_cells.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/invocation_columns.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/invocation_rows.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/leave_prompt.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/log_block.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/log_blocks.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/reload_on_change.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/run_app.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/run_cells.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/run_screen.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/run_tui.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/runs_table.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/tui/stopped_title.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/wait_for_note.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/src/labnotes/watch_rows.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/tests/integration/conftest.py +0 -0
- {labnotes-0.0.104 → labnotes-0.0.106}/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.106
|
|
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>
|
|
@@ -75,8 +75,11 @@ script so it writes its outputs there.
|
|
|
75
75
|
Each param is appended as `--name value`, with the name as written (use
|
|
76
76
|
`source-language` for a `--source-language` flag), booleans as 0 or 1, and lists
|
|
77
77
|
and tables as JSON. labnotes writes `run.json` (params, created, ended, exit
|
|
78
|
-
code), `stdout.log` and `stderr.log` to the
|
|
79
|
-
run folder.
|
|
78
|
+
code, `ended_by`, `signal`), `stdout.log` and `stderr.log` to the
|
|
79
|
+
run folder. `ended_by` is `"timeout"`, `"stop"` (labnotes stopped it),
|
|
80
|
+
`"signal"` (a signal labnotes didn't send, like the out-of-memory killer) or
|
|
81
|
+
null (the script exited itself). `signal` is the signal number read from the
|
|
82
|
+
exit code (negative, or 128 + N from the shell), else null.
|
|
80
83
|
A run counts when it exited 0 and its `run.json` doesn't hold
|
|
81
84
|
`"excluded": true`, so excluding a run by hand means adding that key; the next
|
|
82
85
|
`run` replaces it. Other keys you add to `run.json` are kept and show in `run`.
|
|
@@ -133,7 +136,8 @@ going. Elsewhere, it prints a line per event instead.
|
|
|
133
136
|
- `-e PATH` reads another file instead of `experiment.toml`.
|
|
134
137
|
- `--timeout SECONDS` stops any one run still going after that many seconds:
|
|
135
138
|
SIGTERM, then SIGKILL 5 seconds later. Its
|
|
136
|
-
`run.json` gets `"error": "timed out after ..."`
|
|
139
|
+
`run.json` gets `"error": "timed out after ..."` and `"ended_by": "timeout"`
|
|
140
|
+
next to its exit code.
|
|
137
141
|
Unset, runs have no time limit.
|
|
138
142
|
- `run` hands its runs to the project's labnotes daemon, starting it if
|
|
139
143
|
needed, and shows or waits for them. The daemon listens on a socket in
|
|
@@ -7,6 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
import psutil
|
|
8
8
|
|
|
9
9
|
from labnotes.log_path import LOG_ENV
|
|
10
|
+
from labnotes.runner.exit_signal import exit_signal
|
|
10
11
|
from labnotes.runner.shell_command import shell_command
|
|
11
12
|
from labnotes.runner.stop_then_kill import GRACE_SECONDS
|
|
12
13
|
from labnotes.runner.wait_for_tree import wait_for_tree
|
|
@@ -26,6 +27,7 @@ async def execute_run(
|
|
|
26
27
|
retry_of: str | None = None,
|
|
27
28
|
timeout: float | None = None,
|
|
28
29
|
stop: Callable[[], None] = lambda: None,
|
|
30
|
+
stopped: Callable[[], bool] = lambda: False,
|
|
29
31
|
) -> dict[str, object]:
|
|
30
32
|
run: dict[str, object] = {
|
|
31
33
|
"params": params,
|
|
@@ -33,6 +35,8 @@ async def execute_run(
|
|
|
33
35
|
"ended": None,
|
|
34
36
|
"exit_code": None,
|
|
35
37
|
"error": None,
|
|
38
|
+
"ended_by": None,
|
|
39
|
+
"signal": None,
|
|
36
40
|
}
|
|
37
41
|
if retry_of is not None:
|
|
38
42
|
run["retry_of"] = retry_of
|
|
@@ -70,7 +74,14 @@ async def execute_run(
|
|
|
70
74
|
kill_later = asyncio.get_running_loop().call_later(
|
|
71
75
|
GRACE_SECONDS, stop
|
|
72
76
|
)
|
|
73
|
-
run["exit_code"] = await exited
|
|
77
|
+
run["exit_code"] = code = await exited
|
|
78
|
+
run["signal"] = exit_signal(code)
|
|
79
|
+
if kill_later is not None:
|
|
80
|
+
run["ended_by"] = "timeout"
|
|
81
|
+
elif stopping.is_set() or stopped():
|
|
82
|
+
run["ended_by"] = "stop"
|
|
83
|
+
elif run["signal"] is not None:
|
|
84
|
+
run["ended_by"] = "signal"
|
|
74
85
|
# The shell can exit on SIGTERM before what it started. Once
|
|
75
86
|
# stopping, the run stays killable until everything signalled
|
|
76
87
|
# is gone. A normal exit ends the run even if helpers linger.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import signal
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def exit_signal(exit_code: int | None) -> int | None:
|
|
5
|
+
if exit_code is None:
|
|
6
|
+
return None
|
|
7
|
+
if exit_code < 0:
|
|
8
|
+
return -exit_code
|
|
9
|
+
# A shell reports a child killed by signal N as exit 128 + N.
|
|
10
|
+
number = exit_code - 128
|
|
11
|
+
return number if 0 < number < signal.NSIG else None
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import codecs
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from typing import Any, Self
|
|
3
4
|
|
|
4
5
|
from rich.text import Text
|
|
5
6
|
from textual.geometry import Size
|
|
@@ -10,7 +11,7 @@ STREAMS = ("stdout", "stderr")
|
|
|
10
11
|
|
|
11
12
|
class RunOutput(RichLog):
|
|
12
13
|
def __init__(self, id: str | None = None) -> None:
|
|
13
|
-
super().__init__(id=id)
|
|
14
|
+
super().__init__(id=id, wrap=True, min_width=1)
|
|
14
15
|
self.reset()
|
|
15
16
|
|
|
16
17
|
def reset(self) -> None:
|
|
@@ -21,7 +22,11 @@ class RunOutput(RichLog):
|
|
|
21
22
|
}
|
|
22
23
|
self.last_stream: str | None = None
|
|
23
24
|
self.unfinished = ""
|
|
25
|
+
self.written_rows = 0
|
|
24
26
|
self.clear()
|
|
27
|
+
# A hidden log has no width; writing now would wrap every line to one
|
|
28
|
+
# cell. Hold writes until it's shown and resized.
|
|
29
|
+
self._size_known = bool(self.scrollable_content_region.width)
|
|
25
30
|
|
|
26
31
|
@property
|
|
27
32
|
def mid_line(self) -> bool:
|
|
@@ -42,7 +47,7 @@ class RunOutput(RichLog):
|
|
|
42
47
|
# A stream's unfinished line is redrawn as it grows, but the
|
|
43
48
|
# other stream's output starts below it.
|
|
44
49
|
if self.mid_line and stream == self.last_stream:
|
|
45
|
-
self.
|
|
50
|
+
self.drop_unfinished()
|
|
46
51
|
text = self.unfinished + text
|
|
47
52
|
*complete, self.unfinished = text.split("\n")
|
|
48
53
|
if complete:
|
|
@@ -51,11 +56,19 @@ class RunOutput(RichLog):
|
|
|
51
56
|
self.write(Text.from_ansi(self.unfinished))
|
|
52
57
|
self.last_stream = stream
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
# Counted here, not in follow: writes made before the size is known
|
|
60
|
+
# render later, from on_resize.
|
|
61
|
+
def write(self, content: object, *args: Any, **kwargs: Any) -> Self:
|
|
62
|
+
rows = len(self.lines)
|
|
63
|
+
super().write(content, *args, **kwargs)
|
|
64
|
+
self.written_rows = len(self.lines) - rows
|
|
65
|
+
return self
|
|
66
|
+
|
|
67
|
+
def drop_unfinished(self) -> None:
|
|
55
68
|
if not self._size_known:
|
|
56
69
|
self._deferred_renders.pop()
|
|
57
70
|
return
|
|
58
|
-
del self.lines[-
|
|
71
|
+
del self.lines[-self.written_rows :]
|
|
59
72
|
self._line_cache.clear()
|
|
60
73
|
self.virtual_size = Size(self.virtual_size.width, len(self.lines))
|
|
61
74
|
self.refresh()
|
|
@@ -69,3 +69,9 @@ path = "src/labnotes/runner/run_experiment.py"
|
|
|
69
69
|
rules = ["mutation"]
|
|
70
70
|
lines = [184, 263]
|
|
71
71
|
reason = "a retry is only scheduled below the budget and each one adds one, so tried never passes retries: >= equals ==; both sides are small ints, which CPython caches, so is equals =="
|
|
72
|
+
|
|
73
|
+
[[python.exempt]]
|
|
74
|
+
path = "src/labnotes/runner/exit_signal.py"
|
|
75
|
+
rules = ["mutation"]
|
|
76
|
+
lines = [10]
|
|
77
|
+
reason = "exit_code is non-negative here; when bit 7 is set, - 128 equals ^ 128, and when it is clear ^ 128 lands above NSIG like a non-positive difference"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|