Source code for tbot.machine.connector.common

# tbot, Embedded Automation Tool
# Copyright (C) 2019  Harald Seiler
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.

import abc
import contextlib
import typing

import tbot
import tbot.error
from tbot import machine  # noqa: F401
from .. import channel, linux
from . import connector

M = typing.TypeVar("M", bound="machine.Machine")


[docs]class SubprocessConnector(connector.Connector): """ Connector using a subprocess shell. Arguably the simplest connector; simply spawns a subprocess with a shell. This is the connector used by the default local lab-host. **Example**: .. code-block:: python from tbot.machine import connector, linux class MyMachine( connector.SubprocessConnector, linux.Bash, ): pass with MyMachine() as localhost: localhost.exec0("echo", "Hello!") """
[docs] @classmethod @contextlib.contextmanager def from_context(cls: typing.Type[M], ctx: "tbot.Context") -> typing.Iterator[M]: with cls() as m: yield m
def _connect(self) -> channel.Channel: return channel.SubprocessChannel()
[docs] def clone(self: M) -> M: """Clone this machine.""" new = type(self)() new._orig = self._orig or self return new
[docs]class ConsoleConnector(connector.Connector): """ Connector for serial-consoles. As this can work in many different ways, this connector is intentionally as generic as possible. To configure a serial connection, you need to implement the :py:meth:`ConsoleConnector.connect` method. That methods gets a lab-host channel which it should transform into a channel connected to the board's serial console. **Example**: .. code-block:: python import tbot from tbot.machine import board, connector class MyBoard( connector.ConsoleConnector, board.Board, ): def connect(self, mach): return mach.open_channel("picocom", "-b", "115200", "/dev/ttyACM0") with tbot.acquire_local() as lo: with MyBoard(lo) as b: ... """
[docs] @abc.abstractmethod def connect(self, mach: linux.LinuxShell) -> typing.ContextManager[channel.Channel]: """ Connect a machine to the serial console. Overwrite this method with the necessary logic to connect the given machine ``mach`` to a channel connected to the board's serial console. In most cases you'll accomplish this using :py:meth:`mach.open_channel(...) <tbot.machine.linux.LinuxShell.open_channel>`. """ raise tbot.error.AbstractMethodError()
def __init__(self, mach: linux.LinuxShell) -> None: """ :param LinuxShell mach: A cloneable lab-host machine. The :py:class:`ConsoleConnector` will try to clone this machine's connection and use that to connect to the board. This means that you have to make sure you give the correct lab-host for your respective board to the constructor here. """ self.host = mach
[docs] @classmethod @contextlib.contextmanager def from_context(cls: typing.Type[M], ctx: "tbot.Context") -> typing.Iterator[M]: with contextlib.ExitStack() as cx: lh = cx.enter_context(ctx.request(tbot.role.LabHost)) m = cx.enter_context(cls(lh)) # type: ignore yield typing.cast(M, m)
@contextlib.contextmanager def _connect(self) -> typing.Iterator[channel.Channel]: with self.host.clone() as cloned, self.connect(cloned) as ch: yield ch # yield cloned.open_channel("picocom", "-b", str(115200), "/dev/ttyUSB0")
[docs] def clone(self: M) -> M: """This machine is **not** cloneable.""" raise NotImplementedError("can't clone a serial connection")
class NullConnector(connector.Connector): """ Connector for machine which do not really have a channel. This connector returns a pseudo-channel which will raise Exceptions when accessed. It is meant to be used for machines which do not have a channel, but do provide other means of interaction. For example, think of a board without a serial console. .. versionadded:: 0.9.3 """ def __init__(self) -> None: pass @classmethod @contextlib.contextmanager def from_context(cls: typing.Type[M], ctx: "tbot.Context") -> typing.Iterator[M]: with cls() as m: yield m @contextlib.contextmanager def _connect(self) -> typing.Iterator[channel.Channel]: with channel.NullChannel() as ch: yield ch def clone(self: M) -> M: raise NotImplementedError("can't clone a null connection")