# tbot, Embedded Automation Tool
# Copyright (C) 2018 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 typing
import abc
import shlex
from tbot import machine
from tbot.machine import linux # noqa: F401
H = typing.TypeVar("H", bound="linux.LinuxMachine")
class Special(abc.ABC, typing.Generic[H]):
"""Base class for special characters."""
@abc.abstractmethod
def resolve_string(self, host: H) -> str:
"""Return the string representation of this special character."""
pass
[docs]class Raw(Special):
"""Raw unescaped string."""
__slots__ = ("text",)
def __init__(self, text: str) -> None:
"""
Create a new unescaped string.
**Example**::
m.exec0(linux.Raw('FOOBAR="${USER}@$(hostname):${PWD}"'))
:param str text: The raw string
"""
self.text = text
def resolve_string(self, _: H) -> str:
"""Return the string representation of this special character."""
return self.text
[docs]class F(Special[H]):
"""Format string."""
__slots__ = ("fmt", "args", "quote")
def __init__(
self,
fmt: str,
*args: "typing.Union[str, linux.Path[H], Special[H]]",
quote: bool = True,
) -> None:
"""
Create a format string.
**Example**::
m.exec0("export", linux.F("PATH={}:{}", p1, linux.Env("PATH"), quote=False))
All normal python formatters are supported.
:param str fmt: Format string
:param args: Format arguments. Can be tbot paths as well.
:param bool quote: Whether to escape the resulting string.
"""
self.fmt = fmt
self.args = args
self.quote = quote
def resolve_string(self, host: H) -> str:
"""Return the string representation of this special character."""
def validate(arg: typing.Union[str, linux.Path[H], Special[H]]) -> str:
if isinstance(arg, str):
return arg
elif isinstance(arg, Special):
return arg.resolve_string(host)
elif isinstance(arg, linux.Path):
if arg.host is not host:
raise machine.WrongHostException(host, arg)
string = arg._local_str()
# If quoting is off, we should still quote paths
if not self.quote:
return shlex.quote(string)
else:
return string
else:
raise TypeError(f"{arg!r} is not a supported argument type!")
args = list(map(validate, self.args))
string = self.fmt.format(*args)
if self.quote:
return shlex.quote(string)
else:
return string
[docs]class Env(Special):
"""Expand an environment variable or shell variable."""
__slots__ = ("name",)
def __init__(self, name: str) -> None:
"""
Create a new environment variable accessor.
**Example**::
m.exec0(linux.Env("CC"), "-c", m.workdir / "main.c")
:param str name: Name of the env var.
"""
self.name = name
def resolve_string(self, _: H) -> str:
"""Return the string representation of this special character."""
return f"${{{self.name}}}"
class _Static(Special):
__slots__ = ("string",)
def __init__(self, string: str) -> None:
self.string = string
def resolve_string(self, _: H) -> str:
"""Return the string representation of this special character."""
return self.string
AndThen = _Static("&&")
Background = _Static("&")
OrElse = _Static("||")
Pipe = _Static("|")
Then = _Static(";")