lib.ExecContext: Support bytes-typed cmd_input

The Input instance passed as cmd_input to ExecContext.run() and
.sudo() currently may be of type str. Allow to pass bytes, too.

At the same time, disallow None to be passed as cmd_input. Force the
caller to be more explicit how it wants input to be handled, notably
with respect to interactivity.

Along the way fix a bug: Content in cmd_input should result in
CallContext.interactive == False but doesn't. Fix that.

Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
Jan Lindemann 2026-04-15 14:02:44 +02:00
commit 04b294917f
9 changed files with 46 additions and 29 deletions

View file

@ -142,7 +142,7 @@ class AsyncSSH(Base):
conn: asyncssh.SSHClientConnection,
cmd: list[str],
wd: str | None,
cmd_input: str | None,
cmd_input: bytes | None,
env: dict[str, str] | None,
) -> Result:
command = self._build_remote_command(cmd, wd)
@ -192,7 +192,7 @@ class AsyncSSH(Base):
async def _pump_stdin() -> None:
if cmd_input is not None and proc.stdin is not None:
proc.stdin.write(cmd_input.encode(sys.stdout.encoding or "utf-8"))
proc.stdin.write(cmd_input)
await proc.stdin.drain()
while True:
@ -304,7 +304,7 @@ class AsyncSSH(Base):
cmd: list[str],
wd: str | None,
verbose: bool,
cmd_input: str | None,
cmd_input: bytes | None,
env: dict[str, str] | None,
log_prefix: str,
) -> Result:
@ -336,7 +336,7 @@ class AsyncSSH(Base):
)
if cmd_input is not None and proc.stdin is not None:
proc.stdin.write(cmd_input.encode(sys.stdout.encoding or "utf-8"))
proc.stdin.write(cmd_input)
await proc.stdin.drain()
proc.stdin.write_eof()
@ -356,7 +356,7 @@ class AsyncSSH(Base):
cmd: list[str],
wd: str | None,
verbose: bool,
cmd_input: str | None,
cmd_input: bytes | None,
env: dict[str, str] | None,
interactive: bool,
log_prefix: str,
@ -425,7 +425,7 @@ class AsyncSSH(Base):
]
if cmd_input is not None and proc.stdin is not None:
proc.stdin.write(cmd_input.encode(sys.stdout.encoding or "utf-8"))
proc.stdin.write(cmd_input)
await proc.stdin.drain()
proc.stdin.write_eof()