fix: say a pipe when output goes to a pipe

| tee log.txt was told its output went to a file. The line now names a pipe or a file, and says how to keep the run or answer.
This commit is contained in:
Abdessamad Derraz committed 2026-10-23 23:27:11 +02:00
1 parent 359c260e98
commit 71162d124d
2 files changed
+52 -2

No files matched your search

+18 -2
View File
@@ -9023,6 +9023,18 @@ def _is_terminal(stream: TextIO) -> bool:
return False
def _output_kind(stream: TextIO) -> str:
""""pipe" or "file" for what stream writes to, "" when neither is known.
`| tee log.txt` shows every line, `> log.txt` none of them."""
try:
mode = os.fstat(stream.fileno()).st_mode
except (OSError, ValueError, AttributeError, io.UnsupportedOperation):
return ""
if stat.S_ISFIFO(mode) or stat.S_ISSOCK(mode):
return "pipe"
return "file" if stat.S_ISREG(mode) else ""
def _install(args: argparse.Namespace, real_stdout: TextIO) -> None:
"""One run of the installer, once the options are read."""
KEPT.reset()
@@ -9035,8 +9047,12 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None:
# to a prompt nobody could see.
interactive = False
if not listing and not args.check:
print("Output goes to a file, so nothing is asked: the run goes on as "
"with --no-input.", file=sys.stderr)
kind = _output_kind(real_stdout)
where = f"Output goes to a {kind}" if kind else "Output is not a terminal"
undo = "the pipe" if kind == "pipe" else "the redirection"
print(f"{where}, so nothing is asked: the run goes on as with --no-input. "
f"Add --no-input to say so, or run it without {undo} to answer.",
file=sys.stderr)
print(f"{term.bold('RetroBIOS')} {term.dim(installer_id())}\n")
os_type = detect_os()
+34
View File
@@ -618,6 +618,40 @@ class OutputToAFileAsksNothing(unittest.TestCase):
self.assertNotIn("Enter) install everything", proc.stdout)
self.assertEqual(len(installed), len(FakeRepository.PAYLOADS))
@unittest.skipIf(os.name == "nt", "a pseudo-terminal for stdin")
def test_a_pipe_is_named_a_pipe(self):
"""`install.py | tee log.txt` was told its output went to a file."""
import pty
master, slave = pty.openpty()
self.addCleanup(os.close, master)
self.addCleanup(os.close, slave)
with FakeRepository() as repo:
piped = subprocess.run(
[*repo.command(), "--dry-run"], env=repo.env(), stdin=slave,
capture_output=True, text=True, timeout=60, check=False)
log = repo.root / "log.txt"
with open(log, "w", encoding="utf-8") as handle:
filed = subprocess.run(
[*repo.command(), "--dry-run"], env=repo.env(), stdin=slave,
stdout=handle, stderr=subprocess.PIPE, text=True, timeout=60,
check=False)
self.assertIn("Output goes to a pipe, so nothing is asked", piped.stderr)
self.assertIn("run it without the pipe to answer", piped.stderr)
self.assertIn("Output goes to a file, so nothing is asked", filed.stderr)
self.assertIn("run it without the redirection to answer", filed.stderr)
for proc in (piped, filed):
self.assertIn("Add --no-input to say so", proc.stderr)
def test_the_kind_of_output(self):
self.assertEqual(install._output_kind(io.StringIO()), "")
read, write = os.pipe()
with open(read, "rb") as reader, open(write, "w") as writer:
self.assertEqual(install._output_kind(writer), "pipe")
del reader
with tempfile.TemporaryFile("w", dir=REPO_ROOT / "tmp") as handle:
self.assertEqual(install._output_kind(handle), "file")
class _Tty(io.StringIO):
def isatty(self) -> bool: