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.


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

Name of this machine.

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


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.