Example #1
0
    def _shell_command_full(self):
        """
        Generates a shell command which execution is equal to self.execute()
        including handling of errors in the middle of the process pipe.
        """

        command = BytesIO()

        pipe_ok_statuses = []
        self._shell_command(command, pipe_ok_statuses)

        if len(pipe_ok_statuses) == 1:
            return command.getvalue()

        command.write(b"; statuses=(${PIPESTATUS[@]});")

        for process_id, ok_statuses in enumerate(pipe_ok_statuses):
            if process_id == len(pipe_ok_statuses) - 1:
                command.write(
                    psys.b(" exit ${{statuses[{0}]}};".format(process_id)))
            else:
                command.write(
                    psys.b(" case ${{statuses[{0}]}} in".format(process_id)))
                if ok_statuses:
                    command.write(
                        psys.b(" {0});;".format("|".join(
                            str(status) for status in ok_statuses))))
                command.write(b" *) exit 128;; esac;")

        return b"bash -c '" + command.getvalue().replace(b"'",
                                                         b"""'"'"'""") + b"'"
Example #2
0
    def _shell_command_full(self):
        """
        Generates a shell command which execution is equal to self.execute()
        including handling of errors in the middle of the process pipe.
        """

        command = BytesIO()

        pipe_ok_statuses = []
        self._shell_command(command, pipe_ok_statuses)

        if len(pipe_ok_statuses) == 1:
            return command.getvalue()

        command.write(b"; statuses=(${PIPESTATUS[@]});")

        for process_id, ok_statuses in enumerate(pipe_ok_statuses):
            if process_id == len(pipe_ok_statuses) - 1:
                command.write(psys.b(" exit ${{statuses[{0}]}};".format(process_id)))
            else:
                command.write(psys.b(" case ${{statuses[{0}]}} in".format(process_id)))
                if ok_statuses:
                    command.write(psys.b(" {0});;".format("|".join(str(status) for status in ok_statuses))))
                command.write(b" *) exit 128;; esac;")

        return b"bash -c '" + command.getvalue().replace(b"'", b"""'"'"'""") + b"'"
Example #3
0
def _check_output(obj, valid_stdout, valid_stderr):
    """Checks a program output."""

    stdout = obj.stdout()
    assert type(stdout) == str and stdout == valid_stdout

    stderr = obj.stderr()
    assert type(stderr) == str and stderr == valid_stderr

    raw_stdout = obj.raw_stdout()
    assert type(raw_stdout) == bytes and raw_stdout == psys.b(valid_stdout)

    raw_stderr = obj.raw_stderr()
    assert type(raw_stderr) == bytes and raw_stderr == psys.b(valid_stderr)
Example #4
0
def _check_output(obj, valid_stdout, valid_stderr):
    """Checks a program output."""

    stdout = obj.stdout()
    assert type(stdout) == str and stdout == valid_stdout

    stderr = obj.stderr()
    assert type(stderr) == str and stderr == valid_stderr

    raw_stdout = obj.raw_stdout()
    assert type(raw_stdout) == bytes and raw_stdout == psys.b(valid_stdout)

    raw_stderr = obj.raw_stderr()
    assert type(raw_stderr) == bytes and raw_stderr == psys.b(valid_stderr)
def test_command_with_redirection(test):
    """Tests command arguments representation."""

    process = sh.echo("test", _stdout=STDERR)
    assert str(process) == "echo test >&2"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stdout=File("/tmp/test.path"))
    assert str(process) == "echo test > /tmp/test.path"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stdout=File("/tmp/test.path", append=True))
    assert str(process) == "echo test >> /tmp/test.path"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stderr=File("/tmp/test.path"))
    assert str(process) == "echo test 2> /tmp/test.path"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stderr=File("/tmp/test.path", append=True))
    assert str(process) == "echo test 2>> /tmp/test.path"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stderr=STDOUT)
    assert str(process) == "echo test 2>&1"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stdout=DEVNULL, _stderr=DEVNULL)
    assert str(process) == "echo test > /dev/null 2> /dev/null"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stdout=File("/\rtmp\t/test.\rpath"))
    assert str(process) == "echo test > '/\\rtmp\\t/test.\\rpath'"
    assert bytes(process) == psys.b(str(process))
Example #6
0
File: process.py Project: wd5/psh
        def write_arg(stream, arg):
            arg = psys.b(arg)

            if simple_arg_re.search(arg) is None:
                stream.write(b"'" + arg.replace(b"'", b"""'"'"'""") + b"'")
            else:
                stream.write(arg)
Example #7
0
    def __str__(self):
        """Returns the command string.

        .. note::
            Very lazy formatting.
        """

        return psys.b(self.__unicode__())
Example #8
0
    def __to_bytes(self):
        """Returns the command string.

        .. note::
            Very lazy formatting.
        """

        return psys.b(self.__to_str())
Example #9
0
        def write_arg(stream, arg):
            arg = psys.b(arg)

            if simple_arg_re.search(arg) is None:
                # Or probably we should use br"$'\x{0:02x}'".format(char)
                stream.write(b"'" + arg.replace(b"'", b"""'"'"'""") + b"'")
            else:
                stream.write(arg)
Example #10
0
        def write_arg(stream, arg):
            arg = psys.b(arg)

            if simple_arg_re.search(arg) is None:
                # Or probably we should use br"$'\x{0:02x}'".format(char)
                stream.write(b"'" + arg.replace(b"'", b"""'"'"'""") + b"'")
            else:
                stream.write(arg)
Example #11
0
    def __to_bytes(self):
        """Returns the command string.

        .. note::
            Very lazy formatting.
        """

        return psys.b(self.__to_str())
Example #12
0
def test_string_input(test):
    """Tests process input from string."""

    assert sh.grep("тест",
                   _stdin="aaa\nтест\nbbb\n").execute().stdout() == "тест\n"
    assert sh.grep(
        "тест",
        _stdin=psys.b("aaa\nтест\nbbb\n")).execute().stdout() == "тест\n"
Example #13
0
def test_command_arguments(test):
    """Tests command argument parsing."""

    process = sh.test()
    assert process.command() == [ "test" ]
    assert str(process) == "test"
    assert bytes(process) == psys.b(str(process))

    process = sh.complex_command_name()
    assert process.command() == [ "complex-command-name" ]
    assert str(process) == "complex-command-name"
    assert bytes(process) == psys.b(str(process))

    process = sh("complex command name")("arg1", "arg2")
    assert process.command() == [ "complex command name", "arg1", "arg2" ]
    assert str(process) == "'complex command name' arg1 arg2"
    assert bytes(process) == psys.b(str(process))

    process = sh.test(
        b"arg", b"space arg", b"carriage\rline", b"line\narg", b"tab\targ", br"slash\arg", b"quote'arg", b'quote"arg', psys.b("тест"), psys.b("тест тест"),
        "arg", "space arg", "carriage\rline", "line\narg", "tab\targ", r"slash\arg", "quote'arg", 'quote"arg', "тест", "тест тест",
        3, 2 ** 128, 2.0
    )
    assert process.command() == [ "test",
        b"arg", b"space arg", b"carriage\rline", b"line\narg", b"tab\targ", br"slash\arg", b"quote'arg", b'quote"arg', psys.b("тест"), psys.b("тест тест"),
        "arg", "space arg", "carriage\rline", "line\narg", "tab\targ", r"slash\arg", "quote'arg", 'quote"arg', "тест", "тест тест",
        "3", "340282366920938463463374607431768211456", "2.0"
    ]
    assert str(process) == ("test "
        r"""arg 'space arg' 'carriage\rline' 'line\narg' 'tab\targ' 'slash\\arg' "quote'arg" 'quote"arg' \xd1\x82\xd0\xb5\xd1\x81\xd1\x82 '\xd1\x82\xd0\xb5\xd1\x81\xd1\x82 \xd1\x82\xd0\xb5\xd1\x81\xd1\x82' """
        r"""arg 'space arg' 'carriage\rline' 'line\narg' 'tab\targ' 'slash\\arg' 'quote\'arg' 'quote"arg' тест 'тест тест' """
        "3 340282366920938463463374607431768211456 2.0"
    )
    assert bytes(process) == psys.b(str(process))

    process = sh.test("space arg", s = "short_arg")
    assert process.command() == [ "test", "-s", "short_arg", "space arg" ]
    assert str(process) == "test -s short_arg 'space arg'"
    assert bytes(process) == psys.b(str(process))

    process = sh.test("arg", long_long_arg = "long arg")
    assert process.command() == [ "test", "--long-long-arg", "long arg", "arg" ]
    assert str(process) == "test --long-long-arg 'long arg' arg"
    assert bytes(process) == psys.b(str(process))

    process = sh.test("arg", bool_arg = True)
    assert process.command() == [ "test", "--bool-arg", "arg" ]
    assert str(process) == "test --bool-arg arg"
    assert bytes(process) == psys.b(str(process))

    process = sh.test("arg", bool_arg = False)
    assert process.command() == [ "test", "arg" ]
    assert str(process) == "test arg"
    assert bytes(process) == psys.b(str(process))
def test_output_iteration_with_raw_true(test):
    """Tests iteration over process' output with _iter_raw = True."""

    with sh.cat(_stdin="aaa\nтест\nbbb", _iter_raw=True) as process:
        stdout = [line for line in process]

    assert stdout == [b"aaa\n", psys.b("тест\n"), b"bbb"]

    for line in stdout:
        assert type(line) == bytes
Example #15
0
def test_output_iteration_with_raw_true(test):
    """Tests iteration over process' output with _iter_raw = True."""

    with sh.cat(_stdin="aaa\nтест\nbbb", _iter_raw=True) as process:
        stdout = [line for line in process]

    assert stdout == [b"aaa\n", psys.b("тест\n"), b"bbb"]

    for line in stdout:
        assert type(line) == bytes
Example #16
0
def test_command_with_redirection(test):
    """Tests command arguments representation."""

    process = sh.echo("test", _stdout=STDERR)
    assert str(process) == "echo test >&2"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stdout=File("/tmp/test.path"))
    assert str(process) == "echo test > /tmp/test.path"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stderr=File("/tmp/test.path"))
    assert str(process) == "echo test 2> /tmp/test.path"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stderr=STDOUT)
    assert str(process) == "echo test 2>&1"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stdout=DEVNULL, _stderr=DEVNULL)
    assert str(process) == "echo test > /dev/null 2> /dev/null"
    assert bytes(process) == psys.b(str(process))

    process = sh.echo("test", _stdout=File("/\rtmp\t/test.\rpath"))
    assert str(process) == "echo test > '/\\rtmp\\t/test.\\rpath'"
    assert bytes(process) == psys.b(str(process))
def test_stdin_from_file(test, capfd):
    """Tests redirecting a file to stdin."""

    logging.disable(logging.CRITICAL)

    try:
        with tempfile.NamedTemporaryFile() as temp_file:
            temp_file.write(psys.b("test\nтест"))
            temp_file.flush()

            process = sh.tr("t", "z", _stdin=File(temp_file.name)).execute()

            assert process.stdout() == "zesz\nтест"
            assert process.stderr() == ""

            stdout, stderr = capfd.readouterr()
            assert stdout == ""
            assert stderr == ""
    finally:
        logging.disable(logging.NOTSET)
Example #18
0
def test_stdin_from_file(test, capfd):
    """Tests redirecting a file to stdin."""

    logging.disable(logging.CRITICAL)

    try:
        with tempfile.NamedTemporaryFile() as temp_file:
            temp_file.write(psys.b("test\nтест"))
            temp_file.flush()

            process = sh.tr("t", "z", _stdin=File(temp_file.name)).execute()

            assert process.stdout() == "zesz\nтест"
            assert process.stderr() == ""

            stdout, stderr = capfd.readouterr()
            assert stdout == ""
            assert stderr == ""
    finally:
        logging.disable(logging.NOTSET)
Example #19
0
def test_stdout_to_dev_null_and_stderr_to_file(test, capfd):
    """Tests redirection of stdout to /dev/null and stderr to a file."""

    logging.disable(logging.CRITICAL)

    try:
        with tempfile.NamedTemporaryFile() as temp_file:
            process = sh.sh("-c", "echo test1; echo test2 >&2; echo тест3; echo тест4 >&2;",
                _stdout=DEVNULL, _stderr=File(temp_file.name))
            process.execute()

            assert process.stdout() == ""
            assert process.stderr() == ""

            stdout, stderr = capfd.readouterr()
            assert stdout == ""
            assert stderr == ""

            assert temp_file.read() == psys.b("test2\nтест4\n")
    finally:
        logging.disable(logging.NOTSET)
def test_stdout_to_dev_null_and_stderr_to_file(test, capfd):
    """Tests redirection of stdout to /dev/null and stderr to a file."""

    logging.disable(logging.CRITICAL)

    try:
        with tempfile.NamedTemporaryFile() as temp_file:
            process = sh.sh(
                "-c",
                "echo test1; echo test2 >&2; echo тест3; echo тест4 >&2;",
                _stdout=DEVNULL,
                _stderr=File(temp_file.name))
            process.execute()

            assert process.stdout() == ""
            assert process.stderr() == ""

            stdout, stderr = capfd.readouterr()
            assert stdout == ""
            assert stderr == ""

            assert temp_file.read() == psys.b("test2\nтест4\n")
    finally:
        logging.disable(logging.NOTSET)
Example #21
0
def test_command_arguments(test):
    """Tests command argument parsing."""

    process = sh.test()
    assert process.command() == ["test"]
    assert str(process) == "test"
    assert bytes(process) == psys.b(str(process))

    process = sh.complex_command_name()
    assert process.command() == ["complex-command-name"]
    assert str(process) == "complex-command-name"
    assert bytes(process) == psys.b(str(process))

    process = sh("complex command name")("arg1", "arg2")
    assert process.command() == ["complex command name", "arg1", "arg2"]
    assert str(process) == "'complex command name' arg1 arg2"
    assert bytes(process) == psys.b(str(process))

    process = sh.test(b"arg", b"space arg", b"carriage\rline", b"line\narg",
                      b"tab\targ", br"slash\arg", b"quote'arg", b'quote"arg',
                      psys.b("тест"), psys.b("тест тест"), "arg", "space arg",
                      "carriage\rline", "line\narg", "tab\targ", r"slash\arg",
                      "quote'arg", 'quote"arg', "тест", "тест тест", 3, 2**128,
                      2.0)
    assert process.command() == [
        "test", b"arg", b"space arg", b"carriage\rline", b"line\narg",
        b"tab\targ", br"slash\arg", b"quote'arg", b'quote"arg',
        psys.b("тест"),
        psys.b("тест тест"), "arg", "space arg", "carriage\rline", "line\narg",
        "tab\targ", r"slash\arg", "quote'arg", 'quote"arg', "тест",
        "тест тест", "3", "340282366920938463463374607431768211456", "2.0"
    ]
    assert str(process) == (
        "test "
        r"""arg 'space arg' 'carriage\rline' 'line\narg' 'tab\targ' 'slash\\arg' "quote'arg" 'quote"arg' \xd1\x82\xd0\xb5\xd1\x81\xd1\x82 '\xd1\x82\xd0\xb5\xd1\x81\xd1\x82 \xd1\x82\xd0\xb5\xd1\x81\xd1\x82' """
        r"""arg 'space arg' 'carriage\rline' 'line\narg' 'tab\targ' 'slash\\arg' 'quote\'arg' 'quote"arg' тест 'тест тест' """
        "3 340282366920938463463374607431768211456 2.0")
    assert bytes(process) == psys.b(str(process))

    process = sh.test("space arg", s="short_arg")
    assert process.command() == ["test", "-s", "short_arg", "space arg"]
    assert str(process) == "test -s short_arg 'space arg'"
    assert bytes(process) == psys.b(str(process))

    process = sh.test("arg", long_long_arg="long arg")
    assert process.command() == ["test", "--long-long-arg", "long arg", "arg"]
    assert str(process) == "test --long-long-arg 'long arg' arg"
    assert bytes(process) == psys.b(str(process))

    process = sh.test("arg", bool_arg=True)
    assert process.command() == ["test", "--bool-arg", "arg"]
    assert str(process) == "test --bool-arg arg"
    assert bytes(process) == psys.b(str(process))

    process = sh.test("arg", bool_arg=False)
    assert process.command() == ["test", "arg"]
    assert str(process) == "test arg"
    assert bytes(process) == psys.b(str(process))
Example #22
0
    def __child(self):
        """Handles child process execution."""

        exit_code = 127

        try:
            exec_error = False

            try:
                fd_name = {
                    0: "stdin",
                    1: "stdout",
                    2: "stderr",
                }

                def redirect_fd(path, fd, write=True, append=False):
                    try:
                        if write:
                            file_fd = eintr_retry(
                                os.open)(path, os.O_WRONLY | os.O_CREAT |
                                         (os.O_APPEND if append else 0), 0o666)
                        else:
                            file_fd = eintr_retry(os.open)(path, os.O_RDONLY)

                        try:
                            eintr_retry(os.dup2)(file_fd, fd)
                        finally:
                            eintr_retry(os.close)(file_fd)
                    except Exception as e:
                        raise Error("Unable to redirect {0} to {1}: {2}",
                                    fd_name[fd] if write else "'" + path + "'",
                                    "'" + path + "'" if write else fd_name[fd],
                                    psys.e(e))

                # Connect all pipes
                for pipe in self.__pipes:
                    try:
                        eintr_retry(
                            os.dup2)(pipe.write if pipe.output else pipe.read,
                                     pipe.source)
                    except Exception as e:
                        raise Error("Unable to connect a pipe to {0}: {1}",
                                    fd_name[pipe.source], psys.e(e))

                    pipe.close()

                # Close all file descriptors
                psys.close_all_fds()

                # Configure stdin
                if isinstance(self.__stdin_source, File):
                    redirect_fd(self.__stdin_source.path,
                                psys.STDIN_FILENO,
                                write=False)

                # Configure stdout
                if self.__stdout_target is STDERR:
                    try:
                        eintr_retry(os.dup2)(psys.STDERR_FILENO,
                                             psys.STDOUT_FILENO)
                    except Exception as e:
                        raise Error("Unable to redirect stdout to stderr: {0}",
                                    psys.e(e))
                elif isinstance(self.__stdout_target, File):
                    redirect_fd(self.__stdout_target.path,
                                psys.STDOUT_FILENO,
                                append=self.__stdout_target.append)

                # Configure stderr
                if self.__stderr_target is STDOUT:
                    try:
                        eintr_retry(os.dup2)(psys.STDOUT_FILENO,
                                             psys.STDERR_FILENO)
                    except Exception as e:
                        raise Error("Unable to redirect stderr to stdout: {0}",
                                    psys.e(e))
                elif isinstance(self.__stderr_target, File):
                    redirect_fd(self.__stderr_target.path,
                                psys.STDERR_FILENO,
                                append=self.__stderr_target.append)

                # Required when we have C locale
                command = [psys.b(arg) for arg in self.__command]

                exec_error = True

                if self.__env is None:
                    os.execvp(self.__program, command)
                else:
                    os.execvpe(self.__program, command, self.__env)
            except Exception as e:
                if exec_error and isinstance(
                        e, EnvironmentError) and e.errno == errno.EACCES:
                    exit_code = 126

                print("Failed to execute '{program}': {error}.".format(
                    program=self.__program, error=psys.e(e)),
                      file=sys.stderr)
        finally:
            os._exit(exit_code)
Example #23
0
def test_string_input(test):
    """Tests process input from string."""

    assert sh.grep("тест", _stdin="aaa\nтест\nbbb\n").execute().stdout() == "тест\n"
    assert sh.grep("тест", _stdin=psys.b("aaa\nтест\nbbb\n")).execute().stdout() == "тест\n"
Example #24
0
    def __child(self):
        """Handles child process execution."""

        exit_code = 127

        try:
            exec_error = False

            try:
                fd_name = {
                    0: "stdin",
                    1: "stdout",
                    2: "stderr",
                }

                def redirect_fd(path, fd, write=True, append=False):
                    try:
                        if write:
                            file_fd = eintr_retry(os.open)(
                                path, os.O_WRONLY | os.O_CREAT | (os.O_APPEND if append else 0), 0o666)
                        else:
                            file_fd = eintr_retry(os.open)(path, os.O_RDONLY)

                        try:
                            eintr_retry(os.dup2)(file_fd, fd)
                        finally:
                            eintr_retry(os.close)(file_fd)
                    except Exception as e:
                        raise Error("Unable to redirect {0} to {1}: {2}",
                            fd_name[fd] if write else "'" + path + "'",
                            "'" + path + "'" if write else fd_name[fd], psys.e(e))

                # Connect all pipes
                for pipe in self.__pipes:
                    try:
                        eintr_retry(os.dup2)(pipe.write if pipe.output else pipe.read, pipe.source)
                    except Exception as e:
                        raise Error("Unable to connect a pipe to {0}: {1}",
                            fd_name[pipe.source], psys.e(e))

                    pipe.close()

                # Close all file descriptors
                psys.close_all_fds()

                # Configure stdin
                if isinstance(self.__stdin_source, File):
                    redirect_fd(self.__stdin_source.path, psys.STDIN_FILENO, write=False)

                # Configure stdout
                if self.__stdout_target is STDERR:
                    try:
                        eintr_retry(os.dup2)(psys.STDERR_FILENO, psys.STDOUT_FILENO)
                    except Exception as e:
                        raise Error("Unable to redirect stderr to stdout: {0}", psys.e(e))
                elif isinstance(self.__stdout_target, File):
                    redirect_fd(self.__stdout_target.path, psys.STDOUT_FILENO,
                        append=self.__stdout_target.append)

                # Configure stderr
                if self.__stderr_target is STDOUT:
                    try:
                        eintr_retry(os.dup2)(psys.STDOUT_FILENO, psys.STDERR_FILENO)
                    except Exception as e:
                        raise Error("Unable to redirect stderr to stdout: {0}", psys.e(e))
                elif isinstance(self.__stderr_target, File):
                    redirect_fd(self.__stderr_target.path, psys.STDERR_FILENO,
                        append=self.__stderr_target.append)

                # Required when we have C locale
                command = [psys.b(arg) for arg in self.__command]

                exec_error = True

                if self.__env is None:
                    os.execvp(self.__program, command)
                else:
                    os.execvpe(self.__program, command, self.__env)
            except Exception as e:
                if exec_error and isinstance(e, EnvironmentError) and e.errno == errno.EACCES:
                    exit_code = 126

                print("Failed to execute '{program}': {error}.".format(
                    program=self.__program, error=psys.e(e)), file=sys.stderr)
        finally:
            os._exit(exit_code)
Example #25
0
    def __communicate(self, poll):
        """Communicates with the process and waits for its termination."""

        pipe_map = {}
        poll_objects = 0

        # Configure the poll object and pipes -->
        poll.register(self.__termination_fd, poll.POLLIN)
        poll_objects += 1

        for pipe in self.__pipes:
            fd = pipe.read if pipe.output else pipe.write
            if fd is None:
                continue

            pipe_map[fd] = pipe

            flags = eintr_retry(fcntl.fcntl)(fd, fcntl.F_GETFL)
            eintr_retry(fcntl.fcntl)(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)

            poll.register(fd, poll.POLLIN if pipe.output else poll.POLLOUT)
            pipe.close(read=not pipe.output, write=pipe.output)
            poll_objects += 1
        # Configure the poll object and pipes <--

        # Communicate with the process -->
        stdin = None

        while poll_objects:
            events = poll.poll()

            for fd, flags in events:
                # Process termination
                if fd == self.__termination_fd:
                    if self.__wait_for_output:
                        poll.unregister(self.__termination_fd)
                        poll_objects -= 1
                        continue
                    else:
                        poll_objects = 0
                        break

                pipe = pipe_map[fd]

                # stdin
                if pipe.source == psys.STDIN_FILENO:
                    if stdin is None:
                        try:
                            stdin = next(self.__stdin_generator)

                            try:
                                if type(stdin) not in (bytes, str):
                                    raise TypeError("must be a string")

                                stdin = psys.b(stdin)
                            except Exception as e:
                                raise InvalidArgument(
                                    "Invalid stdin data: {0}", e)
                        except StopIteration:
                            pass
                        except Exception as e:
                            self.__error = e
                            stdin = None

                    if stdin is None:
                        poll.unregister(fd)
                        poll_objects -= 1
                        pipe.close()
                    else:
                        try:
                            size = eintr_retry(os.write)(fd, stdin)
                        except EnvironmentError as e:
                            # The process closed its stdin
                            if e.errno == errno.EPIPE:
                                poll.unregister(fd)
                                poll_objects -= 1
                                pipe.close()
                            else:
                                raise e
                        else:
                            if size == len(stdin):
                                stdin = None
                            else:
                                stdin = stdin[size:]
                # stdout/stderr
                elif pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO):
                    data = eintr_retry(os.read)(fd, psys.BUFSIZE)

                    if data:
                        self.__on_output(pipe.source, data)
                    else:
                        poll.unregister(fd)
                        poll_objects -= 1
                else:
                    raise LogicalError()
        # Communicate with the process <--

        if not self.__wait_for_output:
            # The process has terminated, but we should continue communication
            # to get output that we haven't got from it yet. But we must do it
            # wisely, because the process might fork() itself, so we'll read
            # its child's output forever.

            # Maximum output size after process termination (bigger than any
            # pipe buffer size).
            max_data_size = 1024 * 1024

            for pipe in self.__pipes:
                if (pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO)
                        and pipe.read is not None):
                    size = 0

                    while size < max_data_size:
                        try:
                            data = eintr_retry(os.read)(
                                pipe.read,
                                min(psys.BUFSIZE, max_data_size - size))
                        except EnvironmentError as e:
                            if e.errno != errno.EAGAIN:
                                raise e

                            if not self.__truncate_output:
                                self.__error = ProcessOutputWasTruncated(
                                    str(self), self.__status,
                                    self.__stdout.getvalue(),
                                    self.__stderr.getvalue())

                            break
                        else:
                            if data:
                                size += len(data)
                                self.__on_output(pipe.source, data)
                            else:
                                break
Example #26
0
 def generate_klass(name, subclasses, dict):
     if six.PY2:
         name = psys.b(name)
     else:
         name = psys.u(name)
     return type(name, subclasses, dict)
Example #27
0
    def __parse_args(self, args, options):
        """Parses command arguments and options."""

        # Process options -->
        def check_arg(option, value, types=tuple(), instance_of=tuple(), values=tuple()):
            if type(value) not in types and not isinstance(value, instance_of) and value not in values:
                raise InvalidArgument("Invalid value for option {0}", option)

            return value

        for option, value in options.items():
            if not option.startswith("_"):
                continue

            if option == "_defer":
                self.__defer = check_arg(option, value, types=(bool,))
            elif option == "_env":
                if value is None:
                    self.__env = value
                else:
                    self.__env = dict(
                        (
                            psys.b(check_arg(option, k, types=(bytes, str))),
                            psys.b(check_arg(option, v, types=(bytes, str)))
                        )
                        for k, v in value.items())
            elif option == "_iter_delimiter":
                self.__iter_delimiter = psys.b(
                    check_arg(option, value, types=(bytes, str)))
            elif option == "_iter_raw":
                self.__iter_raw = check_arg(option, value, types=(bool,))
            elif option == "_ok_statuses":
                self.__ok_statuses = [
                    check_arg(option, status, types=(int,)) for status in value]
            elif option == "_on_execute":
                self.__on_execute = check_arg(option, value, instance_of=collections.Callable)
            elif option == "_shell":
                self.__shell = check_arg(option, value, types=(bool,))
            elif option == "_stderr":
                self.__stderr_target = check_arg(
                    option, value, instance_of=File, values=(STDOUT, STDERR, PIPE))
            elif option == "_stdin":
                self.__stdin_source = check_arg(option, value, types=(bytes, str),
                    instance_of=(File, collections.Iterator, collections.Iterable),
                    values=(STDIN,))
            elif option == "_stdout":
                self.__stdout_target = check_arg(
                    option, value, instance_of=File, values=(STDOUT, STDERR, PIPE))
            elif option == "_truncate_output":
                self.__truncate_output = check_arg(option, value, types=(bool,))
            elif option == "_wait_for_output":
                self.__wait_for_output = check_arg(option, value, types=(bool,))
            else:
                raise InvalidArgument("Invalid option: {0}", option)
        # Process options <--

        # Command arguments -->
        self.__command.append(self.__program)

        for option, value in options.items():
            if option.startswith("_"):
                pass
            elif len(option) == 1:
                if value is not False:
                    self.__command.append("-" + option)

                    if value is not True:
                        self.__command.append(_get_arg_value(value, self.__shell))
            else:
                if value is not False:
                    self.__command.append("--" + option.replace("_", "-"))

                    if value is not True:
                        self.__command.append(_get_arg_value(value, self.__shell))

        self.__command += [_get_arg_value(arg, self.__shell) for arg in args]
Example #28
0
    def __parse_args(self, args, options):
        """Parses command arguments and options."""

        # Process options -->
        def check_arg(option,
                      value,
                      types=tuple(),
                      instance_of=tuple(),
                      values=tuple()):
            if type(value) not in types and not isinstance(
                    value, instance_of) and value not in values:
                raise InvalidArgument("Invalid value for option {0}", option)

            return value

        for option, value in options.items():
            if not option.startswith("_"):
                continue

            if option == "_defer":
                self.__defer = check_arg(option, value, types=(bool, ))
            elif option == "_env":
                if value is None:
                    self.__env = value
                else:
                    self.__env = dict(
                        (psys.b(check_arg(option, k, types=(bytes, str))),
                         psys.b(check_arg(option, v, types=(bytes, str))))
                        for k, v in value.items())
            elif option == "_iter_delimiter":
                self.__iter_delimiter = psys.b(
                    check_arg(option, value, types=(bytes, str)))
            elif option == "_iter_raw":
                self.__iter_raw = check_arg(option, value, types=(bool, ))
            elif option == "_ok_statuses":
                self.__ok_statuses = [
                    check_arg(option, status, types=(int, ))
                    for status in value
                ]
            elif option == "_on_execute":
                self.__on_execute = check_arg(option,
                                              value,
                                              instance_of=collections.Callable)
            elif option == "_shell":
                self.__shell = check_arg(option, value, types=(bool, ))
            elif option == "_stderr":
                self.__stderr_target = check_arg(option,
                                                 value,
                                                 instance_of=File,
                                                 values=(STDOUT, STDERR, PIPE))
            elif option == "_stdin":
                self.__stdin_source = check_arg(
                    option,
                    value,
                    types=(bytes, str),
                    instance_of=(File, collections.Iterator,
                                 collections.Iterable),
                    values=(STDIN, ))
            elif option == "_stdout":
                self.__stdout_target = check_arg(option,
                                                 value,
                                                 instance_of=File,
                                                 values=(STDOUT, STDERR, PIPE))
            elif option == "_truncate_output":
                self.__truncate_output = check_arg(option,
                                                   value,
                                                   types=(bool, ))
            elif option == "_wait_for_output":
                self.__wait_for_output = check_arg(option,
                                                   value,
                                                   types=(bool, ))
            else:
                raise InvalidArgument("Invalid option: {0}", option)
        # Process options <--

        # Command arguments -->
        self.__command.append(self.__program)

        for option, value in options.items():
            if option.startswith("_"):
                pass
            elif len(option) == 1:
                if value is not False:
                    self.__command.append("-" + option)

                    if value is not True:
                        self.__command.append(
                            _get_arg_value(value, self.__shell))
            else:
                if value is not False:
                    self.__command.append("--" + option.replace("_", "-"))

                    if value is not True:
                        self.__command.append(
                            _get_arg_value(value, self.__shell))

        self.__command += [_get_arg_value(arg, self.__shell) for arg in args]
Example #29
0
    def __communicate(self, poll):
        """Communicates with the process and waits for its termination."""

        pipe_map = {}
        poll_objects = 0

        # Configure the poll object and pipes -->
        poll.register(self.__termination_fd, poll.POLLIN)
        poll_objects += 1

        for pipe in self.__pipes:
            fd = pipe.read if pipe.output else pipe.write
            if fd is None:
                continue

            pipe_map[fd] = pipe

            flags = eintr_retry(fcntl.fcntl)(fd, fcntl.F_GETFL)
            eintr_retry(fcntl.fcntl)(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)

            poll.register(fd, poll.POLLIN if pipe.output else poll.POLLOUT)
            pipe.close(read=not pipe.output, write=pipe.output)
            poll_objects += 1
        # Configure the poll object and pipes <--

        # Communicate with the process -->
        stdin = None

        while poll_objects:
            events = poll.poll()

            for fd, flags in events:
                # Process termination
                if fd == self.__termination_fd:
                    if self.__wait_for_output:
                        poll.unregister(self.__termination_fd)
                        poll_objects -= 1
                        continue
                    else:
                        poll_objects = 0
                        break

                pipe = pipe_map[fd]

                # stdin
                if pipe.source == psys.STDIN_FILENO:
                    if stdin is None:
                        try:
                            stdin = next(self.__stdin_generator)

                            try:
                                if type(stdin) not in (bytes, str):
                                    raise TypeError("must be a string")

                                stdin = psys.b(stdin)
                            except Exception as e:
                                raise InvalidArgument("Invalid stdin data: {0}", e)
                        except StopIteration:
                            pass
                        except Exception as e:
                            self.__error = e
                            stdin = None

                    if stdin is None:
                        poll.unregister(fd)
                        poll_objects -= 1
                        pipe.close()
                    else:
                        try:
                            size = eintr_retry(os.write)(fd, stdin)
                        except EnvironmentError as e:
                            # The process closed its stdin
                            if e.errno == errno.EPIPE:
                                poll.unregister(fd)
                                poll_objects -= 1
                                pipe.close()
                            else:
                                raise e
                        else:
                            if size == len(stdin):
                                stdin = None
                            else:
                                stdin = stdin[size:]
                # stdout/stderr
                elif pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO):
                    data = eintr_retry(os.read)(fd, psys.BUFSIZE)

                    if data:
                        self.__on_output(pipe.source, data)
                    else:
                        poll.unregister(fd)
                        poll_objects -= 1
                else:
                    raise LogicalError()
        # Communicate with the process <--

        if not self.__wait_for_output:
            # The process has terminated, but we should continue communication
            # to get output that we haven't got from it yet. But we must do it
            # wisely, because the process might fork() itself, so we'll read
            # its child's output forever.

            # Maximum output size after process termination (bigger than any
            # pipe buffer size).
            max_data_size = 1024 * 1024

            for pipe in self.__pipes:
                if (
                    pipe.source in (psys.STDOUT_FILENO, psys.STDERR_FILENO) and
                    pipe.read is not None
                ):
                    size = 0

                    while size < max_data_size:
                        try:
                            data = eintr_retry(os.read)(
                                pipe.read, min(psys.BUFSIZE, max_data_size - size))
                        except EnvironmentError as e:
                            if e.errno != errno.EAGAIN:
                                raise e

                            if not self.__truncate_output:
                                self.__error = ProcessOutputWasTruncated(
                                    str(self), self.__status,
                                    self.__stdout.getvalue(), self.__stderr.getvalue())

                            break
                        else:
                            if data:
                                size += len(data)
                                self.__on_output(pipe.source, data)
                            else:
                                break
Example #30
0
 def generate_klass(name, subclasses, dict):
     if six.PY2:
         name = psys.b(name)
     else:
         name = psys.u(name)
     return type(name, subclasses, dict)