from typing import Sequence
from typing import Callable
from typing import Any
import shutil


def requires_executable(executables: Sequence[str]) -> Callable[..., Callable]:
    """Decorator factory to control optional executables.

    Args:
        executables: Names of executables

    Returns:
        decorator: Either 'noop_decorator' that returns the original function or
            'error_decorator' that raises an OSError and lists absent executables.
    """

    def noop_decorator(function: Callable[..., Any]) -> Callable[..., Any]:
        """Returns function unchanged."""
        return function

    def error_decorator(function: Callable[..., Any]) -> Callable[..., Any]:
        """Raises error."""

        def error(*args, **kwargs) -> OSError:
            error_msg = "Required executables not found in path:"
            for exe_error in exe_errors:
                error_msg += f" {exe_error}"
            raise OSError(error_msg)

        return error

    # Try to find excetubles in path
    exe_errors = []
    for executable in executables:
        if not shutil.which(executable):
            exe_errors.append(executable)

    return error_decorator if len(exe_errors) > 0 else noop_decorator