Machines are tbot’s abstraction over the different computers it can interact with. This includes the machine it is running on (LocalLabHost), the lab-host (LabHost), the device-under-test, as U-Boot (UBootMachine) and as Linux (LinuxMachine). Machines are composed of three different parts:

  1. The Connector which is responsible for establishing a connection to the machine. This can happen in many different ways, for example using Paramiko, by opening a serial-console, etc…

  2. Optionally, any number of Initializer s. These can, step by step, setup the channel into a state until the shell appears. Examples would be the UBootAutobootIntercept (catch the autoboot prompt and press a key) or the LinuxBootLogin (wait for the login prompt and then enter username & password).

  3. The Shell which defines how a testcase can interact with this machine. Typically this is an interface of methods for running commands.

Composition is done using multiple-inheritance, like this:

from tbot.machine import connector, linux

class MyLabHost(
    # The connector:

    # - No initializers here -

    # The shell:
    # Options for the paramiko connector:
    hostname = ""
    username = "tbot-user"

    # We can override anything we like to customize it
    def workdir(self):
        return linux.Workdir.static(self, "/opt/tbot-{self.username}")

Machines are used as context-managers. This means you will later on instantiate the above machine like this (if you were to hardcode the machine. You should probably use tbot’s Configuration mechanism instead):

def footest():
    with MyLabHost() as lh:
        # Run a command:
        lh.exec0("uname", "-a")

For more information about the individual parts of a machine, take a look at tbot.machine.connector, tbot.machine.Initializer, and tbot.machine.shell. Specifically for interacting with Linux machines, you should head over to Linux Shells.


class tbot.machine.Machine[source]

Bases: abc.ABC

Base class for all machines.

This class contains the necessary code to compose the different parts of a machine into a usable class. You won’t need to use it directly in most cases as Connector and Shell both inherit from it.

ch: tbot.machine.channel.channel.Channel

Channel to communicate with this machine.


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.

property name: str

Name of this machine.

By default, the name is derived from the class-name but you might want to customize it.

init() None[source]

An optional hook that allows running some code after the machine is initialized.


class FooUBoot(board.Connector, board.UBootShell):
    name = "foo-u-boot"
    prompt = "=> "

    def init(self):
        self.env("autoload", "no")

        self.env("serverip", "")


class tbot.machine.Initializer[source]

Bases: tbot.machine.machine.Machine

Base-class for machine initializers.

abstract _init_machine() ContextManager[source]

Run this initializer.

Implementations of this method can make use of self.ch as they will run after the connector has succeeded.


More docs for this …

class tbot.machine.PreConnectInitializer[source]

Bases: tbot.machine.machine.Machine

Base-class for pre-connection initializer context.

This initializer is run before connecting to the machine, before tbot.machine.Initializer._init_machine()

abstract _init_pre_connect() ContextManager[source]

Pre-connection initialization context.

class tbot.machine.PostShellInitializer[source]

Bases: tbot.machine.machine.Machine

Base-class for post-shell initializer context.

This initializer is run after tbot.machine.Initializer._init_machine() and after the shell has been initialized.

abstract _init_post_shell() ContextManager[source]

Post-shell initialization context.