Line-buffer remote fiwi output: stdbuf + reconfigure stdout/stderr.

SSH remote command uses stdbuf -oL -eL when available so libc-backed I/O
flushes on newlines; fiwi.py enables line_buffering on TextIO stdout/stderr
for Python prints over pipes.

Made-with: Cursor
This commit is contained in:
Robert McMahon 2026-04-03 13:11:46 -07:00
parent 066dbc1d39
commit bca18ecf1e
2 changed files with 26 additions and 3 deletions

16
fiwi.py
View File

@ -2,11 +2,27 @@
"""Fi-Wi test framework CLI — maps and SSH env files resolve to this files directory."""
import os
import sys
import fiwi.paths as _paths
_paths.configure(os.path.dirname(os.path.abspath(__file__)))
def _prefer_line_buffered_stdio() -> None:
"""Flush stdout/stderr after each newline when attached to a pipe (e.g. SSH capture)."""
for stream in (sys.stdout, sys.stderr):
reconf = getattr(stream, "reconfigure", None)
if reconf is None:
continue
try:
reconf(line_buffering=True)
except (OSError, ValueError, AttributeError):
pass
_prefer_line_buffered_stdio()
from fiwi.cli import main
if __name__ == "__main__":

View File

@ -553,13 +553,20 @@ class SshNode:
Single remote command string for ``bash -lc`` so ``~/`` in ``FIWI_REMOTE_*`` paths expand
on the *remote* host (quoted tilde literals would not).
``-u`` forces unbuffered stdout/stderr so SSH pipe capture (no TTY) still receives
BrainStem and libc output from ``discover`` and similar commands.
``-u`` makes the interpreter unbuffered. When ``stdbuf`` from GNU coreutils is on
``PATH`` (typical on Pi / Fedora), ``stdbuf -oL -eL`` also sets **line-buffered**
libc streams so mixed Python + native (BrainStem) output flushes on newlines over
SSH pipes (no TTY). If ``stdbuf`` is missing, the command falls back to ``-u`` only.
"""
py = cls.remote_path_bash_word(cfg.python)
sc = cls.remote_path_bash_word(cfg.script)
tail = " ".join(shlex.quote(p) for p in remote_args)
return f"{py} -u {sc}" + (f" {tail}" if tail else "")
core = f"{py} -u {sc}" + (f" {tail}" if tail else "")
return (
"if command -v stdbuf >/dev/null 2>&1; then "
f"stdbuf -oL -eL {core}; "
f"else {core}; fi"
)
def _fiwi_cmd_argv(self, cfg: SshNodeConfig, remote_args: List[str]) -> List[str]:
inner = self._fiwi_remote_shell_command(cfg, remote_args)