Shell Client

The local terminal client behind the rmote shell command. It bridges the local standard descriptors to a session that the Vty tool starts on the remote host. See the interactive shell guide for the command line.

async rmote.cli.shell.run_shell(transport, command, python='python3', term=None, escape='~', show_transport_stderr=False, want_pty=True, tls=None)[source]

Open a remote session over transport and bridge the local descriptors.

A terminal on both local descriptors gives the session a pseudo terminal. A redirected input or output gives it pipes, with no raw mode and no escape sequence, so redirected bytes pass through unchanged.

The remote child ends with the session. The escape sequence closes the session, it does not leave the child running.

Parameters:
  • transport (list[Text]) – Transport command, for example ["ssh", "server"]. An empty list starts the interpreter on the local host.

  • command (list[Text]) – Command to run. An empty list starts the login shell of the remote user.

  • python (Text) – Python executable to start at the far end.

  • term (Text | None) – Value of TERM for the remote child. None copies the local value.

  • escape (Text) – Escape character for the closing sequence. An empty string disables it. It applies to a terminal session only.

  • show_transport_stderr (bool) – Pass the stderr of the transport command through, so messages from ssh stay visible while the session runs. Without it, that stream is read by the connection, and its last lines appear in the error of a start that fails.

  • want_pty (bool) – False asks for pipes even when the local descriptors are a terminal. A host without a pseudo terminal needs that.

Return type:

getint

Returns:

Exit status of the remote child.

Raises:

NotImplementedError – The remote host has no terminal to give.

rmote.cli.shell.run(args)[source]

Run a shell using parsed command line arguments.

Return type:

getint

rmote.cli.shell.configure_parser(parser)[source]

Register shell arguments and their handler on a subcommand parser.

Return type:

None

Local terminal handling

class rmote.cli.shell.TerminalMode(fd)[source]

Bases: object

Raw mode for the local terminal, with a restore that always runs.

A terminal left in raw mode gives no echo and no line editing, and the user must repair the session by hand. The restore must therefore run on every exit path, including an exception and a signal.

property active: bool

True while the terminal is in raw mode.

enter()[source]

Switch to raw mode. Do nothing when fd is not a terminal.

Return type:

None

restore()[source]

Put the saved settings back. Repeated calls are safe.

Return type:

None

class rmote.cli.shell.NonBlocking(fd)[source]

Bases: object

Make a descriptor non-blocking and restore its original flags.

The descriptor is shared with the rest of the session, so the flags must go back. Without O_NONBLOCK a full pipe or terminal would stop the whole event loop inside one write.

enter()[source]

Add O_NONBLOCK, unless the descriptor is a regular file.

Return type:

None

restore()[source]

Put the original flags back. Repeated calls are safe.

Return type:

None

rmote.cli.shell.guard_terminal(*restore)[source]

Run every restore function when the process does not leave normally.

A finally block does not run when a signal stops the process, so SIGTERM, SIGHUP and interpreter shutdown need their own path. Call the returned function to release the handlers again.

Return type:

Callable[[], None]

class rmote.cli.shell.EscapeFilter(escape)[source]

Bases: object

Recognize the closing sequence in the local input.

In raw mode every key goes to the remote session, so Ctrl-C cannot stop the client. The escape character works only directly after a line end, the same rule as in ssh. <escape>. closes the session and <escape><escape> sends one literal escape character.

feed(data)[source]

Split data into bytes to forward and a request to close.

Return type:

tuple[bytes, bool]

class rmote.cli.shell.ShellSession(protocol, key, escape=b'')[source]

Bases: object

Bridge between the local descriptors and one remote session.

async pump_input()[source]

Forward local input, then report the end of it.

The end of the local input is not the end of the session. The child can still write, so the session keeps reading its output.

Return type:

None

async pump_output()[source]

Write remote output to stdout until the session ends.

Return type:

None

async run()[source]

Run both pumps until the session or the local input ends.

Return type:

None

async send_resize()[source]

Report the current local window size to the remote terminal.

Return type:

None

stop_watching_resize()[source]

Release the window change handler.

Return type:

None

watch_resize()[source]

Report every later window change. Return False when unsupported.

Return type:

bool

Helpers

rmote.cli.shell.terminal_size(*fds)[source]

Return rows and columns of the first descriptor that is a terminal.

Return type:

tuple[getint, getint]

rmote.cli.shell.regular_file(fd)[source]

True when fd is a regular file.

A selector cannot watch a regular file, and such a file is always ready.

Return type:

bool

async rmote.cli.shell.read_available(fd, size)[source]

Wait until fd is readable, then read up to size bytes.

A regular file is always ready, and a selector cannot watch one, so a redirect from a file is read directly. To wait for it would fail with EINVAL.

Return type:

bytes