Source code for benchbuild.utils

"""
Module handler that makes sure the modules for our commands are build similar
to plumbum. The built modules are only active during a run of an experiment and
get deleted afterwards.
"""
import sys
import logging
from types import ModuleType
from plumbum.commands.base import BoundCommand

__ALIASES__ = {"unionfs": ["unionfs_fuse", "unionfs"]}
LOG = logging.getLogger(__name__)


class ErrorCommand(BoundCommand):
    """
    A command that raises an exception when it gets called.
    This allows us to call the study with experiments who use incorrect imports,
    without the entire study to crash.
    The experiment will fail anyway, but without the entire programm crashing.
    """

    def run(self, *args, **kwargs):
        """Simply raises the AttributeError for a missing command."""
        LOG.error("Unable to import a needed module.")
        raise AttributeError(__name__ + ".cmd")


ERROR = ErrorCommand(__name__ + ".cmd", ErrorCommand.__doc__)


class CommandAlias(ModuleType):
    """Module-hack, adapted from plumbum."""

    __all__ = ()
    __package__ = __name__
    __overrides__ = {}
    __override_all__ = None

    def __getattr__(self, command):
        """Proxy getter for plumbum commands."""
        from os import getenv
        from plumbum import local
        from benchbuild.settings import CFG
        from benchbuild.utils.path import list_to_path
        from benchbuild.utils.path import path_to_list

        check = [command]

        if command in self.__overrides__:
            check = self.__overrides__[command]

        if command in __ALIASES__:
            check = __ALIASES__[command]

        env = CFG["env"].value
        path = path_to_list(getenv("PATH", ""))
        path.extend(env.get("PATH", []))

        libs_path = path_to_list(getenv("LD_LIBRARY_PATH", ""))
        libs_path.extend(env.get("LD_LIBRARY_PATH", []))

        if self.__override_all__ is not None:
            check = [self.__override_all__]

        for alias_command in check:
            try:
                alias_cmd = local[alias_command]
                alias_cmd = alias_cmd.with_env(
                    PATH=list_to_path(path),
                    LD_LIBRARY_PATH=list_to_path(libs_path))
                return alias_cmd
            except AttributeError:
                pass
        LOG.warning("'%s' cannot be found. Import failed.", command)
        return ERROR

    def __getitem__(self, command):
        return self.__getattr__(command)

    __path__ = []
    __file__ = __file__


cmd = CommandAlias(__name__ + ".cmd", CommandAlias.__doc__)
sys.modules[cmd.__name__] = cmd

del sys
del logging
del ModuleType
del CommandAlias
del BoundCommand
del ErrorCommand