Blocking Calls

It is possible to run a command using a blocking call, meaning the code will block until the command is finished and its result is returned. The result is instance of ProcessResult and gives you access to return code, standard output and standard error output.

Example: Blocking call
from pytest_mh import MultihostHost
from pytest_mh.conn import ProcessResult

class ExampleRole(MultihostHost[ExampleDomain]):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

    def say_hello(self) -> ProcessResult:
        """
        Run a single line script code.
        """
        return self.host.conn.run("echo 'Hello World'")

    def say_hello_argv(self) -> ProcessResult:
        """
        Execute command by passing list of arguments.
        """
        return self.host.conn.exec(["echo", "Hello World"])

    def say_script(self) -> ProcessResult:
        """
        Execute a multiline script code.
        """
        return self.host.conn.run(
            """
            set -ex

            echo 'Hello World'
            """
        )

    def say_hello_cat(self) -> ProcessResult:
        """
        You can also pass input data.
        """
        return self.host.conn.run("cat", input="Hello World")

@pytest.mark.topology(...)
def test_hello(example: ExampleRole) -> None:
    result = example.say_hello()
    assert result.stdout == "Hello World"

    result = example.say_hello_argv()
    assert result.stdout == "Hello World"

    result = example.say_hello_script()
    assert result.stdout == "Hello World"

    result = example.say_hello_cat()
    assert result.stdout == "Hello World"

If the command returns a non-zero return code, it is automatically considered a failure and ProcessError is raised. If you don’t want to raise the error or if you want to raise it on different condition, you can overwrite the behaviour with raise_on_error argument.

Example: Do not raise exception on non-zero return code
result = self.host.conn.run("echo 'Hello World'", raise_on_error=False)
if result.rc not in (0, 1):
    # Raise ProcessError if rc was not 0 or 1
    result.throw()

Note

Each command execution is logged in the pytest-mh logger. This can often pollute the logs with a lot of commands whose output result is only really important if it fails. Therefore, you can change the log level to add a log record only if the command yields a non-zero return code.

Example: Custom log level
self.host.conn.run(
    "echo 'Hello World'",
    log_level=ProcessLogLevel.Error
)

See ProcessLogLevel for all available log levels.