<filename>buildtest/utils/shell.py
import shutil

from buildtest.exceptions import BuildTestError


class Shell:
    valid_shells = [
        "bash",
        "sh",
        "zsh",
        "csh",
        "tcsh",
        "/bin/bash",
        "/bin/csh",
        "/bin/sh",
        "/bin/tcsh",
        "/bin/zsh",
        "python",
    ]

    def __init__(self, shell="bash"):
        """The Shell initializer takes an input shell and shell options and split
        string by shell name and options.

        Parameters:

        :param shell: specify shell program and any options passed to shell
        :type shell: str
        """

        # enforce input argument 'shell' to be a string
        if not isinstance(shell, str):
            raise BuildTestError(
                f"Invalid type for input: {shell} must be of type 'str'"
            )

        self.name = shell.split()[0]

        # if input shell is not in list of valid shells we raise error.
        if self.name not in self.valid_shells:
            raise BuildTestError(
                f"Invalid shell: {self.name} select from one of the following shells: {self.valid_shells}"
            )

        self._opts = " ".join(shell.split()[1:])
        self.path = self.name

    @property
    def opts(self):
        """retrieve the shell opts that are set on init, and updated with setter"""
        return self._opts

    @opts.setter
    def opts(self, shell_opts):
        """Override the shell options in class attribute, this would be useful
        when shell options need to change due to change in shell program.
        """
        self._opts = shell_opts
        return self._opts

    @property
    def path(self):
        """This method returns the full path to shell program using ``shutil.which()``
        If shell program is not found we raise an exception. The shebang is
        is updated assuming path is valid which is just adding character '#!'
        in front of path. The return is full path to shell program. This method
        automatically updates the shell path when there is a change in attribute
        self.name

        >>> shell = Shell("bash")
        >>> shell.path
        '/usr/bin/bash'
        >>> shell.name="sh"
        >>> shell.path
        '/usr/bin/sh'

        """
        return self._path

    # Identity functions
    def __str__(self):
        return "[buildtest.shell][%s]" % self.name

    def __repr__(self):
        return self.__str__()

    @path.setter
    def path(self, name):
        """If the user provides a new path with a name, do same checks to
        ensure that it's found.
        """
        path = shutil.which(name)

        # raise an exception if shell program is not found
        if not path:
            raise BuildTestError(f"Can't find program: {name}")

        # Update the name not that we are sure path is found
        self.name = name

        # if input shell is not in list of valid shells we raise error.
        if self.name not in self.valid_shells:
            raise BuildTestError(
                f"Please select one of the following shells: {self.valid_shells}"
            )

        self._path = path

        # shebang is formed by adding the char '#!' with path to program
        self.shebang = f"#!{path}"

    def get(self):
        """Return shell attributes as a dictionary"""
        return {
            "name": self.name,
            "opts": self._opts,
            "path": self._path,
            "shebang": self.shebang,
        }

<filename>buildtest/utils/shell.py
import shutil

from buildtest.exceptions import BuildTestError


class Shell:
    valid_shells = [
        "bash",
        "sh",
        "zsh",
        "csh",
        "tcsh",
        "/bin/bash",
        "/bin/csh",
        "/bin/sh",
        "/bin/tcsh",
        "/bin/zsh",
        "python",
    ]

    def __init__(self, shell="bash"):
        """The Shell initializer takes an input shell and shell options and split
        string by shell name and options.

        Parameters:

        :param shell: specify shell program and any options passed to shell
        :type shell: str
        """

        # enforce input argument 'shell' to be a string
        if not isinstance(shell, str):
            raise BuildTestError(
                f"Invalid type for input: {shell} must be of type 'str'"
            )

        self.name = shell.split()[0]

        # if input shell is not in list of valid shells we raise error.
        if self.name not in self.valid_shells:
            raise BuildTestError(
                f"Invalid shell: {self.name} select from one of the following shells: {self.valid_shells}"
            )

        self._opts = " ".join(shell.split()[1:])
        self.path = self.name

    @property
    def opts(self):
        """retrieve the shell opts that are set on init, and updated with setter"""
        return self._opts

    @opts.setter
    def opts(self, shell_opts):
        """Override the shell options in class attribute, this would be useful
        when shell options need to change due to change in shell program.
        """
        self._opts = shell_opts
        return self._opts

    @property
    def path(self):
        """This method returns the full path to shell program using ``shutil.which()``
        If shell program is not found we raise an exception. The shebang is
        is updated assuming path is valid which is just adding character '#!'
        in front of path. The return is full path to shell program. This method
        automatically updates the shell path when there is a change in attribute
        self.name

        >>> shell = Shell("bash")
        >>> shell.path
        '/usr/bin/bash'
        >>> shell.name="sh"
        >>> shell.path
        '/usr/bin/sh'

        """
        return self._path

    # Identity functions
    def __str__(self):
        return "[buildtest.shell][%s]" % self.name

    def __repr__(self):
        return self.__str__()

    @path.setter
    def path(self, name):
        """If the user provides a new path with a name, do same checks to
        ensure that it's found.
        """
        path = shutil.which(name)

        # raise an exception if shell program is not found
        if not path:
            raise BuildTestError(f"Can't find program: {name}")

        # Update the name not that we are sure path is found
        self.name = name

        # if input shell is not in list of valid shells we raise error.
        if self.name not in self.valid_shells:
            raise BuildTestError(
                f"Please select one of the following shells: {self.valid_shells}"
            )

        self._path = path

        # shebang is formed by adding the char '#!' with path to program
        self.shebang = f"#!{path}"

    def get(self):
        """Return shell attributes as a dictionary"""
        return {
            "name": self.name,
            "opts": self._opts,
            "path": self._path,
            "shebang": self.shebang,
        }

The Shell initializer takes an input shell and shell options and split         string by shell name and options.          Parameters:          :param shell: specify shell program and any options passed to shell         :type shell: str # enforce input argument 'shell' to be a string # if input shell is not in list of valid shells we raise error. retrieve the shell opts that are set on init, and updated with setter Override the shell options in class attribute, this would be useful         when shell options need to change due to change in shell program. This method returns the full path to shell program using ``shutil.which()``         If shell program is not found we raise an exception. The shebang is         is updated assuming path is valid which is just adding character '#!'         in front of path. The return is full path to shell program. This method         automatically updates the shell path when there is a change in attribute         self.name          >>> shell = Shell("bash")         >>> shell.path         '/usr/bin/bash'         >>> shell.name="sh"         >>> shell.path         '/usr/bin/sh' # Identity functions If the user provides a new path with a name, do same checks to         ensure that it's found. # raise an exception if shell program is not found # Update the name not that we are sure path is found # if input shell is not in list of valid shells we raise error. # shebang is formed by adding the char '#!' with path to program Return shell attributes as a dictionary