tbot.machine.shell

Shells are one of the three parts of a machine: The shell defines the methods to interact with the machine. For Linux, the shell will usually be tbot.machine.linux.Bash, a shell implementation for interacting with bash.

tbot does not impose any restrictions on how a shell interface is supposed to look like. It does make sense to keep it close to existing implementations though, as long as that is feasible. Look at the LinuxShell class for inspiration.

All shells inherit from tbot.machine.shell.Shell, the base-class for shells. For very bare-bones usecases, tbot provides the RawShell class.

Interactive Access

A lot of shells have an .interactive() method which allows accessing the machine’s console directly. This works similar to a terminal-emulator like picocom or screen. The default testcases interactive_lab, interactive_board, interactive_uboot, and interactive_linux are using this mechanism, for example.

Base-Class

class tbot.machine.shell.Shell[source]

Bases: Machine

abstract _init_shell() AbstractContextManager[source]

Initialize this shell.

An implementation of this method should return a context manager that, when entered, waits for the shell to appear on the channel and sets up any necessary options. This might include deactivating line-editing, disabling the history, etc.

The most comfortable way to implement this is using contextlib.contextmanager():

class Shell(tbot.machine.shell.Shell):
    @contextlib.contextmanager
    def _init_shell(self):
        try:
            # Wait for shell to appear
            ...

            # Setup options
            ...

            yield None
        finally:
            # Optionally destruct shell
            ...
abstract exec(*args: Any) Any[source]

Run a command using this shell.

This is the only “common” interface tbot expects shells to implement. The exact semantics of running commands are up to the implementor. This especially includes the return value.

Parameters:

*args

.exec() should take the command as one argument per command-line token. For example:

mach.exec("echo", "Hello", "World")

Returns:

The return value should in some way be related to the “output” of the command. For LinuxShell, exec returns a tuple of the return code and console output: Tuple[int, str].

ch: Channel

Channel to communicate with this machine.

Warning

Please refrain from interacting with the channel directly. Instead, write a Shell that wraps around the channel interaction. That way, the state of the channel is only managed in a single place and you won’t have to deal with nasty bugs when multiple parties make assumptions about the state of the channel.

Raw-Shell

class tbot.machine.shell.RawShell[source]

Bases: Machine

Absolute minimum shell implementation.

RawShell attempts to be a minimal shell implementation. It does not make any assumptions about the other end. It is used, for example, for raw board-console access which allows debugging before U-Boot is fully working.

exec(*args: str) None[source]

Just send " ".join(args) to the machine’s channel.

This minimal exec() implementation has no way of reading back the command output.

interactive() None[source]

Connect tbot’s stdio to this machine’s channel. This will allow interactive access to the machine.

ch: Channel

Channel to communicate with this machine.

Warning

Please refrain from interacting with the channel directly. Instead, write a Shell that wraps around the channel interaction. That way, the state of the channel is only managed in a single place and you won’t have to deal with nasty bugs when multiple parties make assumptions about the state of the channel.